Code Monkey home page Code Monkey logo

flake8-pyi's People

Contributors

akuli avatar alexwaygood avatar ambv avatar avasam avatar bryanforbes avatar daverball avatar hauntsaninja avatar jayvdb avatar jellezijlstra avatar kennyballou avatar kkirsche avatar kotlinisland avatar pre-commit-ci[bot] avatar srittau avatar tomasr8 avatar twoertwein avatar xuehaipan avatar ymyzk avatar

Stargazers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

flake8-pyi's Issues

Detect unhashable elements in a set?

In a typeshed PR the other day, a contributor tried to annotate an object with frozenset[list[str]]. It's obviously impossible to have a frozenset of lists, as lists aren't hashable, and so I was surprised that none of the CI checks raised an error.

I'm in two minds about whether adding a check for that here would be a good idea:

  • It would be very difficult for us to reliably detect whether an object is hashable or not
  • Ideally this is something that would be flagged by a type-checker, not a linter
  • BUT it might be difficult for even a type-checker to detect whether or not an object is hashable.
  • AND we could probably fairly easily hardcode a list of common mutable objects that can't be used in sets, frozensets or dict keys. It wouldn't be comprehensive, but it would detect the most common errors.

What do we think? Worth doing?

namedtuple syntax

We could add a check for namedtuple syntax. Warn about collections.namedtuple in stubs, possibly also about using the non-class syntax.

flake8 on `*.pyi` still reports missing docstrings?

Thank you for this plugin, it looks useful! However, I have two questions regarding its use.

  1. After adding it to the pre-commit configuration
      - repo: https://github.com/pycqa/flake8
        rev: 4.0.1
        hooks:
        - id: flake8
          files: ^package/|^tests/|setup.py
          additional_dependencies: [flake8-builtins==1.5.3,flake8-docstrings==1.6.0,flake8-pyi==20.10.0,flake8-rst-docstrings==0.2.3,pep8-naming==0.12.1]
    
    nothing is reported, but then…
  2. Running flake8 from the terminal directly I get a bunch of D100, D101, D102 that the docstrings are missing in all *.pyi files.

Not quite sure yet what’s going on here, but if you can shed some light on this that’d be great!

20.10.0: test suite is failing

I'm trying to package your module as an rpm package. So I'm using the typical PEP517 based build, install and test cycle used on building packages from non-root account.

  • python3 -sBm build -w
  • install .whl file in </install/prefix>
  • run pytest with PYTHONPATH pointing to sitearch and sitelib inside </install/prefix>

Here is pytest output:

+ PYTHONPATH=/home/tkloczko/rpmbuild/BUILDROOT/python-flake8-pyi-20.10.0-2.fc35.x86_64/usr/lib64/python3.8/site-packages:/home/tkloczko/rpmbuild/BUILDROOT/python-flake8-pyi-20.10.0-2.fc35.x86_64/usr/lib/python3.8/site-packages
+ /usr/bin/pytest -ra
=========================================================================== test session starts ============================================================================
platform linux -- Python 3.8.12, pytest-6.2.5, py-1.11.0, pluggy-0.13.1
benchmark: 3.4.1 (defaults: timer=time.perf_counter disable_gc=False min_rounds=5 min_time=0.000005 max_time=1.0 calibration_precision=10 warmup=False warmup_iterations=100000)
Using --randomly-seed=3873507879
rootdir: /home/tkloczko/rpmbuild/BUILD/flake8-pyi-20.10.0
plugins: mock-3.6.1, cov-2.12.1, anyio-3.3.4, flaky-3.7.0, console-scripts-1.2.0, asyncio-0.16.0, freezegun-0.4.2, flake8-1.0.7, rerunfailures-9.1.1, yagot-0.5.0, forked-1.4.0, ordering-0.6, xdist-2.5.0, Faker-10.0.0, benchmark-3.4.1, pyfakefs-4.5.3, datadir-1.3.1, regressions-2.2.0, timeout-2.0.2, randomly-3.10.3, perf-0.10.1, trio-0.7.0, requests-mock-1.9.3, hypothesis-6.31.5, easy-server-0.8.0
collected 15 items

tests/test_pyi.py .....F.......F.                                                                                                                                    [100%]

================================================================================= FAILURES =================================================================================
______________________________________________________________________ PyiTestCase.test_function_def _______________________________________________________________________

self = <tests.test_pyi.PyiTestCase testMethod=test_function_def>

    def test_function_def(self) -> None:
        stdout_lines = (
            '5:5: Y009 Empty body should contain "...", not "pass"',
            '19:5: Y010 Function body must contain only "..."',
            '23:5: Y010 Function body must contain only "..."',
        )
>       self.checkFileOutput("emptyfunctions.pyi", stdout_lines=stdout_lines)

tests/test_pyi.py:182:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/test_pyi.py:83: in checkFileOutput
    check_output(
tests/test_pyi.py:78: in check_output
    self.assertMultiLineEqual(expected, actual)
E   AssertionError: 'empt[85 chars]pyi:19:5: Y010 Function body must contain only[71 chars]..."' != 'empt[85 chars]pyi:10:1: B903 Data class should either be imm[585 chars]..."'
E     emptyfunctions.pyi:5:5: Y009 Empty body should contain "...", not "pass"
E   + emptyfunctions.pyi:10:1: B903 Data class should either be immutable or use __slots__ to save memory. Use collections.namedtuple to generate an immutable class, or enumerate the attributes in a __slot__ declaration in the class to leave attributes mutable.
E   + emptyfunctions.pyi:14:1: B903 Data class should either be immutable or use __slots__ to save memory. Use collections.namedtuple to generate an immutable class, or enumerate the attributes in a __slot__ declaration in the class to leave attributes mutable.
E     emptyfunctions.pyi:19:5: Y010 Function body must contain only "..."
E     emptyfunctions.pyi:23:5: Y010 Function body must contain only "..."
_______________________________________________________________________ PyiTestCase.test_empty_init ________________________________________________________________________

self = <tests.test_pyi.PyiTestCase testMethod=test_empty_init>

    def test_empty_init(self) -> None:
        # should have no output if it's not explicitly selected
>       self.checkFileOutput("emptyinit.pyi", stdout_lines=())

tests/test_pyi.py:186:
_ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _
tests/test_pyi.py:83: in checkFileOutput
    check_output(
tests/test_pyi.py:78: in check_output
    self.assertMultiLineEqual(expected, actual)
E   AssertionError: '' != 'emptyinit.pyi:1:1: B903 Data class should[204 chars]ble.'
E   + emptyinit.pyi:1:1: B903 Data class should either be immutable or use __slots__ to save memory. Use collections.namedtuple to generate an immutable class, or enumerate the attributes in a __slot__ declaration in the class to leave attributes mutable.
========================================================================= short test summary info ==========================================================================
FAILED tests/test_pyi.py::PyiTestCase::test_function_def - AssertionError: 'empt[85 chars]pyi:19:5: Y010 Function body must contain only[71 chars]..."' != 'empt[85 chars...
FAILED tests/test_pyi.py::PyiTestCase::test_empty_init - AssertionError: '' != 'emptyinit.pyi:1:1: B903 Data class should[204 chars]ble.'
====================================================================== 2 failed, 13 passed in 14.35s =======================================================================
error: Bad exit status from /var/tmp/rpm-tmp.mkEK9d (%check)

PEP 586 (Literal) renders Y011 wrong

With literal types, the actual default value becomes relevant to type checkers in some cases (because in effect, two different values are different types).

I believe Y011 should be updated to accept a non-ellipsis default value that matches a Literal type in the annotation of that parameter.

Potential problem with floats

flake8-pyi/pyi.py

Lines 292 to 293 in 4c8a4ca

# anything other than the integer 0 doesn't make much sense
if isinstance(slice_num, ast.Num) and slice_num.n == 0:

The code doesn't check for "integer 0", as the comment says, because 0.0 == 0. It could have a bug similar to #60.

Revisit disabled-by-default errors

I'd like to get rid of the disabled-by-default Y090, Y091, and Y092. This will require a couple of different changes both in typeshed and here, so I'm opening this issue to track them all.

  1. Y090 and Y091
    These are essentially special cases of Y010 (don't put stuff in a function body) that we disabled for compatibility years ago. There are no remaining triggers for these rules in typeshed, so I propose that we get rid of the separate codes and make Y010 trigger on everything you may put in a function body.

  2. Y092 triggers on a lot of existing typeshed code:

stdlib/typing_extensions.pyi:43:26: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:44:23: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:45:22: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:46:26: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:47:29: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:51:25: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:78:21: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:92:27: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:93:24: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:128:33: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:129:31: Y092 Top-level attribute must not have a default value
stdlib/typing_extensions.pyi:130:31: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:49:23: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:50:26: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:51:23: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:52:25: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:54:26: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:55:26: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:56:22: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:57:26: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:58:26: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:60:27: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:62:29: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:90:33: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:91:31: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:92:31: Y092 Top-level attribute must not have a default value
stdlib/typing.pyi:136:31: Y092 Top-level attribute must not have a default value
stdlib/@python2/asyncore.pyi:9:24: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:37:26: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:38:23: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:42:25: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:62:21: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:73:27: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:74:24: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:103:29: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:104:27: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing_extensions.pyi:105:27: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:25:23: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:26:26: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:27:23: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:28:25: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:29:26: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:30:26: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:31:22: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:32:26: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:33:23: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:39:25: Y092 Top-level attribute must not have a default value
stdlib/@python2/typing.pyi:41:21: Y092 Top-level attribute must not have a default value
stdlib/@python2/bdb.pyi:8:38: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:4:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:5:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:7:20: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:8:23: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:9:20: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:11:13: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:13:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:14:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:15:17: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:16:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:17:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:18:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:19:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:20:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:21:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:22:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:23:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:24:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:25:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:26:14: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:27:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:28:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:29:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:30:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:31:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:32:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:33:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:34:17: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:35:17: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:36:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:37:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:38:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:39:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:40:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:41:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:42:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:43:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:44:15: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:45:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:46:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:47:18: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:48:17: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:49:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:50:16: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:53:21: Y092 Top-level attribute must not have a default value
stdlib/@python2/signal.pyi:54:25: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:26:20: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:27:19: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:28:21: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:54:29: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:56:37: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:57:37: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:58:31: Y092 Top-level attribute must not have a default value
stdlib/@python2/ctypes/__init__.pyi:59:20: Y092 Top-level attribute must not have a default value
stdlib/@python2/dbm/ndbm.pyi:10:16: Y092 Top-level attribute must not have a default value
stubs/paramiko/paramiko/pkey.pyi:6:29: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:4:25: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:5:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:6:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:7:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:8:22: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:9:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:10:30: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:12:31: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:13:29: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:14:21: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:15:22: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:16:30: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:17:27: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:18:21: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:19:22: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:20:20: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:21:22: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:22:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:23:22: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:24:24: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:25:26: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:27:25: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:28:32: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:29:40: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:30:37: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:31:35: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:32:31: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:33:29: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:34:42: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:35:40: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:36:36: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:37:30: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:38:32: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:39:30: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:40:30: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:41:26: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:42:31: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:43:35: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:44:55: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:45:33: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:46:36: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:47:33: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:48:35: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:49:33: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:50:32: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:51:47: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:52:46: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:53:50: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:54:48: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:55:43: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:57:26: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:58:26: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:59:23: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:61:32: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:62:31: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:63:33: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:64:32: Y092 Top-level attribute must not have a default value
stubs/pyaudio/pyaudio.pyi:65:31: Y092 Top-level attribute must not have a default value
stubs/cryptography/cryptography/x509/oid.pyi:102:43: Y092 Top-level attribute must not have a default value
stubs/cryptography/cryptography/x509/oid.pyi:104:67: Y092 Top-level attribute must not have a default value
stubs/setuptools/pkg_resources/__init__.pyi:41:27: Y092 Top-level attribute must not have a default value

These are mostly of the form constant: type = ..., and they seem legitimate to me. I'll send a PR to typeshed to get rid of the = .... After that we can consider turning it on by default.

Port typeshed's tests/check_new_syntax.py here

Typeshed currently has a script that checks a bunch of things that belong here IMO: https://github.com/python/typeshed/blob/af8e37d2736d3a6fbed8f5dfad71580dd417c612/tests/check_new_syntax.py

  • Collections classes in typing: typing.Counter --> collections.Counter etc (related: #46)
  • Built-in generics: List --> list etc
  • typing.(Async)ContextManager --> contextlib.Abstract(Async)ContextManager
  • Preferring typing over typing_extensions when available: typing_extensions.ClassVar --> typing.ClassVar etc
  • Union[str, int] --> str | int (#45)
  • Banning from collections.abc import Set, because it's confusingly different from the built-in set
  • Ordering sys.version_info checks so that the most recent version is first
  • Ban explicitly inheriting from object in Python 3-only stubs.
  • Disallow using typing.Text in Python 3-only stubs.

We should probably create one error code per Python version, so that it's easy to e.g. enable errors for not using features new in Python 3.8+. Another option would be to make the errors depend on sys.version_info (maybe too clever, would need good documentation).

Never quote annotations

In stubs you can always write def f() -> X: ... instead of def f() -> 'X': .... We could detect this and lint against it.

This would be straightforward to implement in flake8-pyi but might require a fair amount of migration in typeshed.

New check for top-level fields with default value

It would be useful to have a check for top-level attributes that have a PEP 526 variable annotation and a default value:

# ok
foo: int
foo = ...  # type: int

# not ok
foo: int = 0
foo: int = ...
foo = 0  # type: int

Improve error message for chained comparisons

We currently have a very specific test for chained comparisons on lines 506-508:

flake8-pyi/pyi.py

Lines 506 to 508 in 34c17bd

if len(node.comparators) != 1:
# mypy doesn't support chained comparisons
self.error(node, Y002)

However, we then give a generic error message:

"Y002 If test must be a simple comparison against sys.platform or sys.version_info"

If we split this off into its own error code, we could give a much more specific error message:

YXXX Chained comparisons are not supported by type-checkers

Thoughts?

Detect unused type aliases

Now that we've added Y018, it should be fairly easy to add a similar check to detect unused type aliases.

flake8-pyi is not compatible with flake8 3.8

Attempting to run flake8-pyi with flake8 3.8 (which hasn't been released yet) raises an AttributeError due to the use of optparse instead of argparse.

$ pip install flake8==3.8.0a2 flake8-pyi
$ flake8
...
File ".../lib/python3.8/site-packages/pyi.py", line 359, in add_options
    option.to_optparse().default = option.default
File ".../lib/python3.8/site-packages/flake8/options/manager.py", line 332, in to_optparse
    raise AttributeError("to_optparse: flake8 now uses argparse")
AttributeError: to_optparse: flake8 now uses argparse

New check "Default values must be ..."?

Currently there is check Y011 'Default values for typed arguments must be "..."'. I'd like to have a stronger version of that that warns about any default argument that is not .... These are often easily convertible to the preferred ellipsis form. (E.g. foo=0 -> foo: int = ...')

I can provide a pull request if desired.

Incompatibility with black

Black reformats pyi files.
The format is not compatible with flak8-pyi.
Is there a way to establish a friendship between these very useful tools?

I have no configuration except max-line-length = 88.
As a workaround, I've disabled the plugin for yarl project but love to re-enable it back.

Tighter body checks

Y010 currently allows raise statements and assignments in __init__. Since I just stumbled about a few raise statements in typeshed, I'd like a tighter version of this check that disallows anything but ....

Not compatible with flake8 >= 3.8.0

jreese@jreese-mbp ~/scratch » python3.8 -m venv flake8-venv
jreese@jreese-mbp ~/scratch » source flake8-venv/bin/activate
(flake8-venv) jreese@jreese-mbp ~/scratch » pip install flake8==3.8.1 flake8-pyi==19.3.0
Collecting flake8==3.8.1
  Using cached https://files.pythonhosted.org/packages/bf/47/36e51603431e1a5289eb41636199d2c225fcb1ca286e29c02d219c8e6e88/flake8-3.8.1-py2.py3-none-any.whl
Collecting flake8-pyi==19.3.0
  Using cached https://files.pythonhosted.org/packages/cb/77/894472548bbf67ff3a4b9d2208b8a7beb58414112ad77b7d96a792a7d7c5/flake8_pyi-19.3.0-py2.py3-none-any.whl
Collecting mccabe<0.7.0,>=0.6.0 (from flake8==3.8.1)
  Using cached https://files.pythonhosted.org/packages/87/89/479dc97e18549e21354893e4ee4ef36db1d237534982482c3681ee6e7b57/mccabe-0.6.1-py2.py3-none-any.whl
Collecting pycodestyle<2.7.0,>=2.6.0a1 (from flake8==3.8.1)
  Using cached https://files.pythonhosted.org/packages/10/5b/88879fb861ab79aef45c7e199cae3ef7af487b5603dcb363517a50602dd7/pycodestyle-2.6.0-py2.py3-none-any.whl
Collecting pyflakes<2.3.0,>=2.2.0 (from flake8==3.8.1)
  Using cached https://files.pythonhosted.org/packages/69/5b/fd01b0c696f2f9a6d2c839883b642493b431f28fa32b29abc465ef675473/pyflakes-2.2.0-py2.py3-none-any.whl
Collecting attrs (from flake8-pyi==19.3.0)
  Using cached https://files.pythonhosted.org/packages/a2/db/4313ab3be961f7a763066401fb77f7748373b6094076ae2bda2806988af6/attrs-19.3.0-py2.py3-none-any.whl
Installing collected packages: mccabe, pycodestyle, pyflakes, flake8, attrs, flake8-pyi
Successfully installed attrs-19.3.0 flake8-3.8.1 flake8-pyi-19.3.0 mccabe-0.6.1 pycodestyle-2.6.0 pyflakes-2.2.0
(flake8-venv) jreese@jreese-mbp ~/scratch » flake8 --version
Traceback (most recent call last):
  File "/Users/jreese/scratch/flake8-venv/bin/flake8", line 10, in <module>
    sys.exit(main())
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/main/cli.py", line 22, in main
    app.run(argv)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/main/application.py", line 360, in run
    self._run(argv)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/main/application.py", line 347, in _run
    self.initialize(argv)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/main/application.py", line 329, in initialize
    self.register_plugin_options()
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/main/application.py", line 165, in register_plugin_options
    self.check_plugins.register_options(self.option_manager)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/plugins/manager.py", line 494, in register_options
    list(self.manager.map(register_and_enable))
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/plugins/manager.py", line 302, in map
    yield func(self.plugins[name], *args, **kwargs)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/plugins/manager.py", line 490, in register_and_enable
    call_register_options(plugin)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/plugins/manager.py", line 402, in generated_function
    return method(optmanager, *args, **kwargs)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/plugins/manager.py", line 216, in register_options
    add_options(optmanager)
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/pyi.py", line 359, in add_options
    option.to_optparse().default = option.default
  File "/Users/jreese/scratch/flake8-venv/lib/python3.8/site-packages/flake8/options/manager.py", line 332, in to_optparse
    raise AttributeError("to_optparse: flake8 now uses argparse")
AttributeError: to_optparse: flake8 now uses argparse

Two tests fail

When trying to package flake8-pyi-18.3.1 for openSUSE, I get these two tests failing:

[    3s] + py.test3 -v -k 'not ()' tests
[    3s] ============================= test session starts ==============================
[    3s] platform linux -- Python 3.7.1, pytest-3.8.1, py-1.5.4, pluggy-0.7.1 -- /usr/bin/python3
[    3s] cachedir: .pytest_cache
[    3s] rootdir: /home/abuild/rpmbuild/BUILD/flake8-pyi-18.3.1, inifile:
[    3s] collecting ... collected 11 items
[    3s] 
[    4s] tests/test_pyi.py::PyiTestCase::test_comparisons PASSED                  [  9%]
[    4s] tests/test_pyi.py::PyiTestCase::test_defaults FAILED                     [ 18%]
[    5s] tests/test_pyi.py::PyiTestCase::test_empty_init PASSED                   [ 27%]
[    5s] tests/test_pyi.py::PyiTestCase::test_function_def FAILED                 [ 36%]
[    6s] tests/test_pyi.py::PyiTestCase::test_patched_flake8_clean_del PASSED     [ 45%]
[    6s] tests/test_pyi.py::PyiTestCase::test_patched_flake8_clean_forward_refs PASSED [ 54%]
[    6s] tests/test_pyi.py::PyiTestCase::test_sys_platform PASSED                 [ 63%]
[    7s] tests/test_pyi.py::PyiTestCase::test_sys_versioninfo PASSED              [ 72%]
[    7s] tests/test_pyi.py::PyiTestCase::test_typevar PASSED                      [ 81%]
[    7s] tests/test_pyi.py::PyiTestCase::test_vanilla_flake8_not_clean_del PASSED [ 90%]
[    8s] tests/test_pyi.py::PyiTestCase::test_vanilla_flake8_not_clean_forward_refs PASSED [100%]
[    8s] 
[    8s] =================================== FAILURES ===================================
[    8s] __________________________ PyiTestCase.test_defaults ___________________________
[    8s] 
[    8s] self = <tests.test_pyi.PyiTestCase testMethod=test_defaults>
[    8s] 
[    8s]     def test_defaults(self) -> None:
[    8s]         stdout_lines = (
[    8s]             '3:17: Y011 Default values for typed arguments must be "..."',
[    8s]             '7:20: Y011 Default values for typed arguments must be "..."',
[    8s]             '13:20: Y011 Default values for typed arguments must be "..."',
[    8s]         )
[    8s] >       self.checkFileOutput('defaults.pyi', stdout_lines=stdout_lines)
[    8s] 
[    8s] tests/test_pyi.py:141: 
[    8s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[    8s] tests/test_pyi.py:46: in checkFileOutput
[    8s]     self.assertMultiLineEqual(expected, actual)
[    8s] E   AssertionError: 'defaults.pyi:3:17: Y011 Default values for typed argument[160 chars]..."' != 'defaults.pyi:3:1: E302 expected 2 blank lines, found 0\nd[555 chars]nd 0'
[    8s] E   + defaults.pyi:3:1: E302 expected 2 blank lines, found 0
[    8s] E     defaults.pyi:3:17: Y011 Default values for typed arguments must be "..."
[    8s] E   + defaults.pyi:5:1: E302 expected 2 blank lines, found 0
[    8s] E   + defaults.pyi:7:1: E302 expected 2 blank lines, found 0
[    8s] E     defaults.pyi:7:20: Y011 Default values for typed arguments must be "..."
[    8s] E   + defaults.pyi:9:1: E302 expected 2 blank lines, found 0
[    8s] E   + defaults.pyi:11:1: E302 expected 2 blank lines, found 0
[    8s] E   + defaults.pyi:13:1: E302 expected 2 blank lines, found 0
[    8s] E   - defaults.pyi:13:20: Y011 Default values for typed arguments must be "..."+ defaults.pyi:13:20: Y011 Default values for typed arguments must be "..."
[    8s] E   ?                                                                          +
[    8s] E   + defaults.pyi:15:1: E302 expected 2 blank lines, found 0
[    8s] ________________________ PyiTestCase.test_function_def _________________________
[    8s] 
[    8s] self = <tests.test_pyi.PyiTestCase testMethod=test_function_def>
[    8s] 
[    8s]     def test_function_def(self) -> None:
[    8s]         stdout_lines = (
[    8s]             '5:5: Y009 Empty body should contain "...", not "pass"',
[    8s]             '19:5: Y010 Function body must contain only "..."',
[    8s]             '23:5: Y010 Function body must contain only "..."',
[    8s]         )
[    8s] >       self.checkFileOutput('emptyfunctions.pyi', stdout_lines=stdout_lines)
[    8s] 
[    8s] tests/test_pyi.py:124: 
[    8s] _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ _ 
[    8s] tests/test_pyi.py:46: in checkFileOutput
[    8s]     self.assertMultiLineEqual(expected, actual)
[    8s] E   AssertionError: 'emptyfunctions.pyi:5:5: Y009 Empty body should contain "..."[146 chars]..."' != 'emptyfunctions.pyi:4:1: E302 expected 2 blank lines, found 1[522 chars]..."'
[    8s] E   + emptyfunctions.pyi:4:1: E302 expected 2 blank lines, found 1
[    8s] E     emptyfunctions.pyi:5:5: Y009 Empty body should contain "...", not "pass"
[    8s] E   + emptyfunctions.pyi:7:1: E302 expected 2 blank lines, found 1
[    8s] E   + emptyfunctions.pyi:10:1: E302 expected 2 blank lines, found 1
[    8s] E   + emptyfunctions.pyi:14:1: E302 expected 2 blank lines, found 1
[    8s] E   + emptyfunctions.pyi:18:1: E302 expected 2 blank lines, found 1
[    8s] E     emptyfunctions.pyi:19:5: Y010 Function body must contain only "..."
[    8s] E   + emptyfunctions.pyi:21:1: E302 expected 2 blank lines, found 1
[    8s] E     emptyfunctions.pyi:23:5: Y010 Function body must contain only "..."
[    8s] ====================== 2 failed, 9 passed in 4.55 seconds ======================

Full build log

Are these issues which have been addressed since 18.3.1 was released? If yes, which patches I should add? Thank you.

__all__ check

Sometimes, __all__ gets a type annotation in stubs. __all__ in stubs has the same semantics as in implementation files, so an annotation like __all__: List[str] is not very useful.

Squash and merge

#50 was merged without squash and merge, probably because the dropdown doesn't have it selected by default, unless this isn't your first time merging and your previous merge was a "Squash and merge".

I suggest disabling the default non-squash and merge button, like in typeshed. I don't seem to have permissions to tweak the settings though.

Detect module-level constants

An ALL_CAPS_NAME in the global namespace should usually be annotated with Final or (ideally) Literal, especially if it's a str, bytes or int value. It should be fairly easy to detect module-level constants that don't comply with this rule.

Suggest using PEP 604-style union types

Humble suggestion: add new code to suggest a replacement of Union[...] types with the | chain, e.g. Y093 Use PEP 604 union types. A simple implementation start could be e.g.

class PyVisitor(ast.NodeVisitor):
    ...
     def visit_FunctionDef(self, node):
        if node.returns.value is not None and node.returns.value.id in ("Optional", "Union"):
            self.error(node.returns, Y093)
        ...

    def visit_arguments(self, node):
        if arg.annotation.value.id in ("Optional", "Union"):
            self.error(arg, Y093)
        ...

Add LICENSE to sdist

Hi, would you mind adding the LICENSE to the sdist.

This can be done using MANIFEST.in, however I believe in the next release it can also be done by configuring setuptools a little. See pypa/setuptools#1636

Compatibility with flake8 --extend-select

It doesn't work:

% python -m flake8 --select=Y091 tests/raise.pyi
tests/raise.pyi:4:9: Y091 Function body must not contain "raise"
tests/raise.pyi:7:9: Y091 Function body must not contain "raise"
% python -m flake8 --extend-select=Y091 tests/raise.pyi
% 

Improve `README.rst`

The README isn't great at the moment, in my opinion:

  1. It's too long
  2. It's not clear whether it's targeted towards users or contributors
  3. Some parts of it feel like to-do lists for the maintainers that:
    a. aren't of much interest to users of the package; and
    b. should be issues, but aren't.

I plan some initial steps to resolve (1) and (2):

  1. Move the detailed changelog to a CHANGELOG.rst file: #70
  2. Move information on running tests to a CONTRIBUTING.rst file: #69

Things still to do:

  • #72
  • Tweak the "authors" section of the README.

Create and distribute new release?

I have noticed a release for this project hasn't been made in a while. If/when #13 (add .flake8 to manifest file) is merged, can a new release be made available? This would be very helpful as I'm trying to package this project in Guix, however, currently, I'm unable to keep the test suite enabled in the project's current state.

Check for unused imports

A check that knows the special import handling in stubs. E.g.:

from x import a, b, c as c

x: b

This should complain about a being unused, but not b or c.

Check AsyncFunctionDef

I noticed that we have a bunch of checks in visit_FunctionDef, but we don't do the same checks for async functions.

Suggest using collections.abc etc, not typing (deprecated since 3.9)

Affects:

  • AbstractSet (as collections.abc.Set)
  • AsyncContextManager (as contextlib.AbstractAsyncContextManager)
  • AsyncGenerator
  • AsyncIterable
  • AsyncIterator
  • Awaitable
  • ByteString
  • Callable
  • ChainMap (as collections.ChainMap)
  • Collection
  • Container
  • ContextManager (as contextlib.AbstractContextManager)
  • Coroutine
  • Counter (as collections.Counter)
  • DefaultDict (as collections.defaultdict)
  • Deque (as collections.Deque)
  • Generator
  • Hashable
  • ItemsView
  • Iterable
  • Iterator
  • KeysView
  • Mapping
  • MappingView
  • Match (as re.Match)
  • MutableMapping
  • MutableSequence
  • MutableSet
  • OrderedDict (as collections.OrderedDict)
  • Pattern (as re.Pattern)
  • Reversible
  • Sequence
  • Sized
  • ValuesView

Incorrect "F821 undefined name" error

When run with this file:
https://github.com/markshannon/typeshed/blob/362bedecd9778196e44d4c88acc39313bb38a6ba/stdlib/2/os/path.pyi
I get the following failures:

flake8 stdlib/2/os/path.pyi
stdlib/2/os/path.pyi:28:18: F821 undefined name 'EitherStr'
stdlib/2/os/path.pyi:29:19: F821 undefined name 'EitherStr'
etc.

but with --no-pyi-aware-file-checker
it is OK.

flake8 --no-pyi-aware-file-checker  stdlib/2/os/path.pyi

It is not clear to me whether this is a flake8-pyi or a flake8 issue, but it appears to be the former.

Improve code readability

I'd like to make several changes to pyi.py to improve code readability. These are all quite small changes, but I'll submit them as separate PRs, to make them easier to review:

  1. Use the class-based syntax for the Error namedtuple: #65
  2. Reduce the use of unnecessary abbreviations in the code: #66
  3. PyiVisitor._in_class is quite poorly named: it sounds like it's a boolean value from the name, but it's actually an integer that tells you the level of class nesting the visitor is in: #64
  4. I'd like to make several small changes to increase the readability of the control flow: #67

Additional changes I'd like to see:

I also like the new @attrs.define API much more than the legacy API. I'd quite like to refactor pyi.py to use the newer API; however, that would mean we'd have to pin attrs to >=21.3.0. Thoughts on that?

  • ^Let's just use dataclasses and drop support for 3.6, as discussed below: #78
  • Maybe it would be nice to increase the number of docstrings and reduce the number of comments.

Release 22.1.0 planning

We've made a lot of improvements, so I'd like to make a release soon so we can start using them in typeshed.

I can make the actual release, but here's what I'd like to do first:

  • Set up automation (#114)
  • Organize the changelog a bit better

Let me know if there's something else you'd like to go in first! In the future, we should be able to make releases basically whenever we have something we'd like to go out.

Improve documentation for how to reformat large codebases?

Refs discussion in #101. Ideas include:

  • Linking to flake8 docs on writing the config file to disable certain checks.
  • Linking to typeshed PRs that used codemodding scripts.
  • Adding a scripts subdirectory with some example codemodding scripts (but with a big red warning that it's untested code, and might have unpredictable consequences).

Keeping this package alive

This package can be very helpful for keeping stub files healthy, but development has kind of stalled. For example, typeshed now has some lint-type checks in a custom file (check_new_syntax.py). I'd like to keep such checks in this package in the future, so they can be reused by other repos than typeshed and so we can take advantage of the flake8 ecosystem (e.g., standardized noqa comments).

Here's a few process improvements I'm thinking of:

  • More maintainers. I think @srittau is already a maintainer, but @Akuli @hauntsaninja @AlexWaygood would you be interested in joining as maintainers?
  • Making releases easier. Perhaps we should set up a CI pipeline that automatically releases every merged PR to PyPI, so we can quickly upgrade over at typeshed.
  • Moving this repo over to a more general org. @ambv, maybe it can go into github.com/psf?

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    🖖 Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. 📊📈🎉

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google ❤️ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.