Giter Club home page Giter Club logo

mypy-zope's People

Contributors

agroszer avatar arnimarj avatar clokep avatar dependabot-preview[bot] avatar dependabot[bot] avatar euresti avatar gjo avatar graingert avatar jaraco avatar jaroel avatar jugmac00 avatar kedder avatar masipcat avatar recursing avatar regebro 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

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar  avatar

mypy-zope's Issues

Flag incomplete implementation on class def instead of instantiation?

From the README:

class IAnimal(zope.interface.Interface):
    def say() -> None:
        pass

@zope.interface.implementer(IAnimal)
class Cow(object):
    def say(self) -> None:
        print("Moooo")

The interface IAnimal will be treated as an abstract base class of the implementation Cow: you will not be able to instantiate Cow if interface implementation is incomplete. Incompatible method signatures will be reported as well.

If Cow states it implements IAnimal, and it doesn't implement say(), wouldn't that be an error even if Cow is never instantiated? In other words, I think this error could reported on the class definition instead of on instantiation.

Reporting the error on the implementer's class definition would be an advantage in code bases that don't have 100% type checking coverage: if the code that instantiates the implementer is inside a function that lacks type annotations, mypy will skip that code and the incomplete interface implementation will not be reported.

Potential issue with mypy==0.970-dev

I was experimenting with the latest dev release of mypy 0.970 and hit this error:

Traceback (most recent call last):
  File "mypy/semanal.py", line 5350, in accept
  File "mypy/nodes.py", line 1029, in accept
  File "mypy/semanal.py", line 1123, in visit_class_def
  File "mypy/semanal.py", line 1203, in analyze_class
  File "mypy/semanal.py", line 1212, in analyze_class_body_common
  File "mypy/semanal.py", line 1243, in apply_class_plugin_hooks
  File "/home/arni/.config/dohop/tools_venv3/lib/python3.10/site-packages/mypy_zope/plugin.py", line 301, in analyze
    apply_implementer(iface_arg, classdef_ctx.cls.info, api)
  File "/home/arni/.config/dohop/tools_venv3/lib/python3.10/site-packages/mypy_zope/plugin.py", line 293, in apply_implementer
    self._apply_interface(class_info, iface_type)
  File "/home/arni/.config/dohop/tools_venv3/lib/python3.10/site-packages/mypy_zope/plugin.py", line 700, in _apply_interface
    faketi._promote = promote
TypeError: list object expected; got mypy.types.Instance

Subclasses of Twisted's protocol fail to be determined by mypy-zope

Thanks for making mypy-zope! We've been using it for Synapse (which uses Twisted) and ran into a hiccup.

The following will cause mypy-zope to emit an error:

error: Cannot determine consistent method resolution order (MRO) for "Foo" [misc]

from twisted.internet import protocol

class Foo(protocol.Protocol):
    pass

reveal_type(Foo)

The class hierarchy looks like Foo -> Protocol (which implements IProtocol and ILoggingContext) -> BaseProtocol.

I tried to create a reduced test-case for this, but was not able to, so it might be specific to something Twisted has done. Any help would be appreciated!

convert a protocol to an interface

it should be possible to create a zope.interface.Interface by introspecting a typing.Protocol

class SomeProtocol(typing.Protocol):
    @property
    def foo(self) -> str: ...

    def ham(self) -> int: ...

ISome = mypy_zope.fromProtocol("ISome", SomeProtocol)

InterfaceClass subclass not recognized as interface

The project foolscap relies on zope.interface and creates a RemoteInterface as an instance of a subclass of zope.interface.interface.InterfaceClass. When tested against mypy with this plugin, the interface isn't recognized as such an results in "Method must have at least one argument" failed validations.

As an example:

draft $ cat > mypy.ini
[mypy]
ignore_missing_imports = True
plugins=mypy_zope:plugin
draft $ cat > example.py
from foolscap.api import RemoteInterface

      
class Api(RemoteInterface):
    def open():
        "open something"
draft $ pip-run -q mypy foolscap mypy-zope -- -m mypy example.py
example.py:5: error: Method must have at least one argument
Found 1 error in 1 file (checked 1 source file)

I've attempted to create an even more minimal example that doesn't involve foolscap, but I've been unsuccessful:

draft $ cat > example.py
from zope.interface import interface


RemoteInterface = interface.InterfaceClass("RemoteInterface")


class Api(RemoteInterface):
    def open():
        "open something"
draft $ pip-run -q mypy mypy-zope -- -m mypy example.py
example.py:7: error: Variable "example.RemoteInterface" is not valid as a type
example.py:7: note: See https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases
example.py:7: error: Invalid base class "RemoteInterface"
example.py:8: error: Method must have at least one argument
Found 3 errors in 1 file (checked 1 source file)

I don't understand why

Variable "example.RemoteInterface" is not valid as a type

The note there isn't particularly helpful, as I'm creating neither an alias nor a variable. I'm creating a proper type and in the same way that foolscap does, so why does this example error where foolscap doesn't?

And surely the example error isn't valid until mypy recogognizes RemoteInterface correctly.

Can you help to determine a solution here to support foolscap.api.RemoteInterface subclasses within mypy?

type checking for attributes

This example doesn't complain:

from zope.interface import Interface, Attribute, implementer

class IWhatever(Interface):
    stuff: str = Attribute("name")


@implementer(IWhatever)
class WhateverBroken(object):
    stuff: int

even though the type of stuff doesn't match.

This closed PR, from 2017, suggests that this syntax was intended to work one day:

zopefoundation/zope.interface#98

I'm guessing this is what the README is already talking about, when it says "Interface compatibility checker will not type-check non-method attributes"?

Retuning an instance of the class being defined which provides an interface is not handled

In twisted/klein#471, I'm running into the following error in CI:

src/klein/_dihttp.py:62:16: error: Incompatible return value type (got "RequestURL", expected "IDependencyInjector")  [return-value]
            return cls()

The code in question is:

@provider(IRequiredParameter, IDependencyInjector)
class RequestURL:
    """
    Require a hyperlink L{DecodedURL} object from a L{Requirer}.

    @since: Klein NEXT
    """

    @classmethod
    def registerInjector(
        cls,
        injectionComponents: Componentized,
        parameterName: str,
        requestLifecycle: IRequestLifecycle,
    ) -> IDependencyInjector:
        return cls()

…

So here, we return a RequestURL, which is declared as a provider of IDependencyInjector, so I think the above error is incorrect.

I'm guessing this is due to the fact that the definition of RequestURL isn't complete yet?

Metaclass related internal error

Hi,

We just ran into this stacktrace:

https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.800
Traceback (most recent call last):
  File "mypy/semanal.py", line 4835, in accept
  File "mypy/nodes.py", line 950, in accept
  File "mypy/semanal.py", line 1048, in visit_class_def
  File "mypy/semanal.py", line 1125, in analyze_class
  File "mypy/semanal.py", line 1134, in analyze_class_body_common
  File "mypy/semanal.py", line 1187, in apply_class_plugin_hooks
  File "/home/travis/virtualenv/python3.8.1/lib/python3.8/site-packages/mypy_zope/plugin.py", line 266, in analyze_metaclass
    if info and any(node.fullname == expected for node in info.mro):
AttributeError: 'Var' object has no attribute 'mro'

I haven't had time find exactly which piece of code is causing this, but will do so as soon as I can.

EDIT: this was tested on mypy 0.800. The line which hit this was something like this:

@attr.s(slots=True, frozen=True)
class Leg(metaclass=CustomMetaclass):
	a: str = attr.ib()
	b: str = attr.ib()
	c: str = attr.ib()

some way to define self types

eg I might want to define

class ICopyable(zope.interface.Interface):
    def copy(self: T) -> T:
        pass

but I get "Interface methods should not have 'self' argument"

Crash in analysis

Hi, and thanks for a wonderful plugin. We've started using it to check our codebase, and have stumbled upon an issue. When type-checking parts of the pyrsistent library this happened:

/home/arni/dohop/bug/test-mypy-zope/lib/python3.7/site-packages/pyrsistent/_field_common.py:192: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.720
Traceback (most recent call last):
  File "mypy/newsemanal/semanal.py", line 4550, in accept
  File "mypy/nodes.py", line 921, in accept__Node_glue
  File "mypy/nodes.py", line 922, in accept
  File "mypy/newsemanal/semanal.py", line 989, in visit_class_def__StatementVisitor_glue
  File "mypy/newsemanal/semanal.py", line 992, in visit_class_def
  File "mypy/newsemanal/semanal.py", line 1063, in analyze_class
  File "mypy/newsemanal/semanal.py", line 1072, in analyze_class_body_common
  File "mypy/newsemanal/semanal.py", line 1131, in apply_class_plugin_hooks
  File "/home/arni/dohop/bug/test-mypy-zope/lib/python3.7/site-packages/mypy_zope/plugin.py", line 287, in analyze_subinterface
    base_node = api.lookup_fully_qualified_or_none(base_name)
  File "mypy/newsemanal/semanal.py", line 4016, in lookup_fully_qualified_or_none
AssertionError: 
/home/arni/dohop/bug/test-mypy-zope/lib/python3.7/site-packages/pyrsistent/_field_common.py:192: : note: use --pdb to drop into pdb

This happened when running under Pyhton 3.7.4, and can be reproduced like so:

#!/bin/bash

set -e

cat <<EOM >mypy.ini
[mypy]
show_traceback = True
plugins = mypy_zope:plugin
EOM


python -m venv test-mypy-zope
./test-mypy-zope/bin/pip install mypy==0.720 pyrsistent==0.15.4 mypy-zope==0.2.0
./test-mypy-zope/bin/mypy --show-traceback --config mypy.ini bug.py

upgrade to mypy 740?

Looks like this is still pinned to 073; just wanted to file this, partially to ask if anyone knows it works? :)

Is there a way to declare Interface intersections?

I'm trying to use the new types coming out of Twisted in particular the IReactorTCP and IReactorTime but having a bit of an issue with how they are declared. Here's a distilled example:

from zope.interface import implementer
from zope.interface import Interface
from zope.interface import classImplements

class IFoo(Interface):
    x: int

class IBar(Interface):
    y: int

def foo_it(obj: IFoo) -> int:
    return obj.x

def bar_it(obj: IBar) -> int:
    return obj.y

# An attempt at combining IFoo and IBar into one type.
class IFooBar(IFoo, IBar):
    ...

def both_it(obj: IFooBar) -> int:
    assert IFooBar.providedBy(obj)
    return foo_it(obj) + bar_it(obj)

@implementer(IFoo, IBar)
class FooBar:
    def __init__(self, x: int, y: int) -> None:
        self.x = x
        self.y = y

# This would make IFooBar.providedBy(obj) but doesn't help mypy
classImplements(FooBar, IFooBar)

obj = FooBar(6, 7)
foo_it(obj)
bar_it(obj)
both_it(obj)   # ERROR: Argument 1 to "both_it" has incompatible type "FooBar"; expected "IFooBar"

So I thought, ah this is what Protocols in mypy were made for:

from typing import Protocol
class IFooBarProtoco(IFoo, IBar, Protocol):
    ...

Which sadly gives error: All bases of a protocol must be protocols

Is there another way to possibly declare the intersection? Or maybe the plugin can do something special with the Protocol declaration? (But is that even possible)

There's a mypy issue about it too: python/typing#21 but it doesn't provide any solutions.

Follow semantic versioning in mypy version pins

I think until mypy reached 1.0.0 it was a fine policy to pin to the mypy version that was released at the time, since the plugin system is not guaranteed to be stable and the version numbering was sort of semantic, but not really, since it used number digits rather, than numbers separated by dots.

But now that mypy is following semantic versioning rules I think you should consider only pinning the mypy version to before the next minor version, so if the current version was 1.0.0, you would pin to <1.1.0, rather than ==1.0.0, so patch releases, like 1.0.1, which will often contain important bug fixes can still be installed without having to force ignore the version pin.

Consider using pytest-mypy-plugins

Hi! Thanks for this awesome project!

I am TypedDjango team member, we maintain types for, well, django. And we do pretty much the same job.

For example, we also test our types the similar way as you do in tests/. We even created a tool called pytest-mypy-plugins (announcing post) to help us with this task. Maybe it will be also helpful to you as well.

That's how the simplest test looks like:

- case: compose_two_wrong_functions
  main: |
    from returns.functions import compose

    def first(num: int) -> float:
        return float(num)

    def second(num: float) -> str:
        return str(num)

    reveal_type(compose(first, second)(1))  # N: builtins.str*

Ask any questions you have!

P.S. This project was listed in awesome-python-stubs list.

mypy 1.7 support

currently holding back some upgrades, so filing this for tracking

Support for variable / alias types.

Not sure if this is a mypy-zope or mypy issue.

It looks like variable vs type aliases is not supported by mypy-zope

I am looking at the folloing mypy documentation
https://mypy.readthedocs.io/en/latest/common_issues.html#variables-vs-type-aliases

I have the following code:

class _IWaker(Interface):
    """
    Interface to wake up the event loop based on the self-pipe trick.

    The U{I{self-pipe trick}<http://cr.yp.to/docs/selfpipe.html>}, used to wake
    up the main loop from another thread or a signal handler.
    This is why we have wakeUp together with doRead

    This is used by threads or signals to wake up the event loop.
    """
    disconnected = Attribute('')

    def wakeUp():
        """
        Called when the event should be wake up.
        """

    def doRead():
        """
        Read some data from my connection and discard it.
        """

    def connectionLost(reason: failure.Failure):
        """
        Called when connection was closed and the pipes.
        """


@implementer(_IWaker)
class _SocketWaker(log.Logger):
    disconnected = 0

    def __init__(self, reactor):
        """Initialize.
        """

    def wakeUp(self):
        """Send a byte to my connection.
        """


    def doRead(self):
        """
        Read some data from my connection.
        """


    def connectionLost(self, reason):


class _FDWaker(log.Logger, object):
    disconnected = 0

    def __init__(self, reactor):
        """Initialize.
        """


    def doRead(self):
        """
        Read some bytes from the pipe and discard them.
        """


    def connectionLost(self, reason):
        """Close both ends of my pipe.
        """


@implementer(_IWaker)
class _UnixWaker(_FDWaker):
    def wakeUp(self):
        """Write one byte to the pipe, and flush it.
        """


_Waker: _IWaker
if platformType == 'posix':
    _Waker = _UnixWaker
else:
    # Primarily Windows and Jython.
    _Waker = _SocketWaker

with mypy I got the following error:

src/twisted/internet/posixbase.py:228:14: error: Incompatible types in assignment (expression has type "Type[_UnixWaker]", variable has type "_IWaker")  [assignment]
src/twisted/internet/posixbase.py:231:14: error: Incompatible types in assignment (expression has type "Type[_SocketWaker]", variable has type "_IWaker")  [assignment]

I am expecting this not to raise any issue.

Thanks!

Doesn't quite understand that dynamically created classes are classes

Python allows you to create new classes using the three-argument form of the type() builtin. I can't tell if it's a mypy-zope problem or a mypy problem, but this doesn't typecheck properly:

import zope.interface
from zope.interface import Interface, Attribute, declarations

class IFoo(Interface):
    x = Attribute("Some thing or other")

class NormalClass:
    x = 12
declarations.classImplements(NormalClass, IFoo)

print(list(zope.interface.implementedBy(NormalClass)))

DynamicClass = type("DynamicClass", (object,), { "x": 42 })
declarations.classImplements(DynamicClass, IFoo)

print(list(zope.interface.implementedBy(DynamicClass)))

In this example NormalClass and DynamicClass are equivalent (except for their names and the initial value of x). At runtime they behave similarly and the classImplements() calls do what you'd think. Mypy however produces this error:

error: dyntest.DynamicClass is not a class, cannot mark it as a interface implementation  [misc]

TypeError: interpreted classes cannot inherit from compiled

CI is failing, and I believe this means that this plugin doesn't support the current version of mypy. So one bug here is just that setup.py is insufficiently restrictive as the code stands today; it needs an upper bound on mypy versions. But as I recall this is an upstream incompatibility introduced in mypy - which version does it break in, and does this require changes in mypy to start working again or is it something this plugin should change?

Error importing plugin "mypy_zope": cannot import name 'SemanticAnalyzerPass2' from 'mypy.semanal'

Tried to update to mypy 0.942, but I started seeing this:

mypy.ini:4: error: Error importing plugin "mypy_zope": cannot import name 'SemanticAnalyzerPass2' from 'mypy.semanal' (/home/runner/.cache/pre-commit/repo_fawoait/py_env-python3.8/lib/python3.8/site-packages/mypy/semanal.cpython-38-x86_64-linux-gnu.so)  [misc]

I do see a green build on #68, which is a bit curious.

Is there any more info I can provide to help debug this? My current workaround it to keep mypy at 0.941.

Mypy's `--warn-unreachable` option erroneously considers some `isinstance` calls to be always False with mypy-zope 0.9.0

With mypy 1.0.0 and mypy-zope 0.9.0, the typechecker seems to think that that isinstance(x, T) is always false when T implements some interface Iand x: Optional[I]. For example, the source

from typing import Optional, reveal_type
from zope.interface import implementer, Interface


class IFoo(Interface):
    ...


@implementer(IFoo)
class MyFoo:
    ...


def make_foo() -> Optional[IFoo]:
    return MyFoo()


x = make_foo()
reveal_type(x)
assert isinstance(x, MyFoo)
print("hello")

yields

$ mypy temp.py
temp.py:19: note: Revealed type is "Union[temp.IFoo, None]"
temp.py:21: error: Statement is unreachable  [unreachable]
Found 1 error in 1 file (checked 1 source file)

but at runtime, we see that the print statement is reachable:

$ python temp.py
Runtime type is 'MyFoo'
hello

Would guess that #90 is related to this somehow.

mypy doesn't find stubs when on PYTHONPATH

Probably an upstream issue, but first encountered here, so reporting the details. Running a check on a file importing zope.interface will result in an error "Skipping analyzing zope.interface".

draft $ cat mypy.ini
[mypy]
plugins=mypy_zope:plugin
draft $ cat > interface.py
from zope.interface.interface import Interface

   
class X(Interface):
    pass
draft $ python -m venv venv
draft $ venv/bin/pip install -q mypy-zope
draft $ venv/bin/pip uninstall -q -y mypy-zope
draft $ venv/bin/pip install -q -t libs --no-deps mypy-zope
draft $ env PYTHONPATH=libs venv/bin/python -m mypy interface.py
interface.py:1: error: Skipping analyzing 'zope.interface.interface': found module but no type hints or library stubs
interface.py:1: note: See https://mypy.readthedocs.io/en/latest/running_mypy.html#missing-imports
Found 1 error in 1 file (checked 1 source file)

Even though the stubs are there:

draft $ tree libs/zope-stubs/
libs/zope-stubs/
├── __init__.pyi
├── interface
│   ├── __init__.pyi
│   ├── _compat.pyi
│   ├── _flatten.pyi
│   ├── _zope_interface_coptimizations.pyi
│   ├── adapter.pyi
│   ├── advice.pyi
│   ├── common
│   │   ├── __init__.pyi
│   │   ├── idatetime.pyi
│   │   ├── interfaces.pyi
│   │   ├── mapping.pyi
│   │   └── sequence.pyi
│   ├── declarations.pyi
│   ├── document.pyi
│   ├── exceptions.pyi
│   ├── interface.pyi
│   ├── interfaces.pyi
│   ├── registry.pyi
│   ├── ro.pyi
│   └── verify.pyi
└── schema
    ├── __init__.pyi
    ├── _bootstrapfields.pyi
    ├── _bootstrapinterfaces.pyi
    ├── _compat.pyi
    ├── _field.pyi
    ├── _messageid.pyi
    ├── _schema.pyi
    ├── accessors.pyi
    ├── fieldproperty.pyi
    ├── interfaces.pyi
    └── vocabulary.pyi

3 directories, 31 files

Installing the plugin directly into the virtualenv seems to work fine.

draft $ venv/bin/pip install -q mypy-zope
draft $ venv/bin/python -m mypy interface.py
Success: no issues found in 1 source file

Types for zope.component

(Maybe on your TODO list already but if not…) The zope.interface type stubs are just what I needed! If you've got some time, it'd be great to see zope.component get some type stubs as well.

INTERNAL ERROR when enabling mypy-zope for Twisted Klein

In commit twisted/klein@04a536c, I enabled mypy-zope for Twisted Klein and I'm getting an INTERNAL ERROR in the build.

Here is the error with --show-traceback added:

wsanchez$ tox -e mypy -- --show-traceback src
GLOB sdist-make: /Users/wsanchez/Dropbox/Developer/Twisted/klein/setup.py
mypy inst-nodeps: /Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/.tmp/package/1/klein-20.6.0.zip
mypy installed: attrs==20.3.0,Automat==20.2.0,characteristic==14.3.0,constantly==15.1.0,hyperlink==21.0.0,idna==3.1,incremental==17.5.0,klein @ file:///Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/.tmp/package/1/klein-20.6.0.zip,mypy==0.812,mypy-extensions==0.4.3,mypy-zope==0.2.11,PyHamcrest==2.0.2,six==1.15.0,Tubes==0.2.0,Twisted==20.3.0,typed-ast==1.4.2,typing-extensions==3.7.4.3,Werkzeug==1.0.1,zope.event==4.5.0,zope.interface==5.2.0,zope.schema==6.1.0
mypy run-test-pre: PYTHONHASHSEED='3790822665'
mypy run-test: commands[0] | mypy --cache-dir=/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy_cache --pretty --show-traceback src
src/klein/test/test_trial.py:33: error: INTERNAL ERROR -- Please try using mypy master on Github:
https://mypy.rtfd.io/en/latest/common_issues.html#using-a-development-mypy-build
Please report a bug at https://github.com/python/mypy/issues
version: 0.812
Traceback (most recent call last):
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/bin/mypy", line 8, in <module>
    sys.exit(console_entry())
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/__main__.py", line 11, in console_entry
    main(None, sys.stdout, sys.stderr)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/main.py", line 90, in main
    res = build.build(sources, options, None, flush_errors, fscache, stdout, stderr)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 179, in build
    result = _build(
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 253, in _build
    graph = dispatch(sources, manager, stdout)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 2638, in dispatch
    process_graph(graph, manager)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 2962, in process_graph
    process_stale_scc(graph, scc, manager)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 3060, in process_stale_scc
    graph[id].type_check_first_pass()
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/build.py", line 2130, in type_check_first_pass
    self.type_checker().check_first_pass()
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 294, in check_first_pass
    self.accept(d)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 401, in accept
    stmt.accept(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/nodes.py", line 950, in accept
    return visitor.visit_class_def(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 1723, in visit_class_def
    self.accept(defn.defs)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 401, in accept
    stmt.accept(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/nodes.py", line 1015, in accept
    return visitor.visit_block(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 1972, in visit_block
    self.accept(s)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 401, in accept
    stmt.accept(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/nodes.py", line 950, in accept
    return visitor.visit_class_def(self)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checker.py", line 1749, in visit_class_def
    sig, _ = self.expr_checker.check_call(dec, [temp],
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checkexpr.py", line 930, in check_call
    result = self.check_call(call_function, args, arg_kinds, context, arg_names,
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checkexpr.py", line 911, in check_call
    return self.check_callable_call(callee, args, arg_kinds, context, arg_names,
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checkexpr.py", line 1023, in check_callable_call
    new_ret_type = self.apply_function_plugin(
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy/checkexpr.py", line 732, in apply_function_plugin
    return method_callback(
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy_zope/plugin.py", line 171, in analyze_implementation
    iface_type = self._lookup_type(ifacename, method_ctx.api)
  File "/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/lib/python3.9/site-packages/mypy_zope/plugin.py", line 583, in _lookup_type
    module = api.modules[module_name]
KeyError: 'klein.test.test_trial.TestCaseTests'
src/klein/test/test_trial.py:33: : note: use --pdb to drop into pdb
ERROR: InvocationError for command /Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy/bin/mypy --cache-dir=/Users/wsanchez/Dropbox/Developer/Twisted/klein/.tox/mypy_cache --pretty --show-traceback src (exited with code 2)
__________________________________________ summary __________________________________________
ERROR:   mypy: commands failed

Interfaces don't play well with @overload

They sort of work, in that calls to @overloaded functions of an interface type are typechecked correctly, but there are two problems:

  • Mypy produces errors like error: Self argument missing for a non-static method (or an invalid type for self)
  • I haven't been able to declare a class that implements an interface with an overloaded method without getting a mypy error like error: Signature of "MySomething" incompatible with "getStuff" of supertype "ISomething".

Here's a gist demonstrating the situation I'm talking about.

Interestingly, the actual uses of both the interface and the concrete class seem to be typechecked correctly at least some of the time: the assignments to variables i and j are satisfactory to mypy, even though they wouldn't be without the @overloads.

(Using mypy==0.812 mypy-extensions==0.4.3 mypy-zope==0.2.13 )

error: implementer accepts interface, not {interface subclass}

I think I may have stumbled onto another defect in mypy.zope. Consider this code:

from zope.interface import implementer
from twisted.cred.credentials import ICredentials


class IToken(ICredentials):
    pass


@implementer(IToken)
class Token:
    pass

Run that through mypy with this mypy.ini:

[mypy]
ignore_missing_imports = True
plugins=mypy_zope:plugin

With twisted and zope interface installed, and this error occurs:

$ .tox/typechecks/bin/python -m mypy --no-incremental creds.py
creds.py:9: error: zope.interface.implementer accepts interface, not creds.IToken.
creds.py:9: error: Make sure you have stubs for all packages that provide interfaces for creds.IToken class hierarchy.
Found 2 errors in 1 file (checked 1 source file)

But make either of these tweaks and the errors go away:

  1. Save the implementer in a separate variable.
from zope.interface import implementer
from twisted.cred.credentials import ICredentials


class IToken(ICredentials):
    pass


_impl = implementer(IToken)

@_impl
class Token:
    pass
  1. Inline the definition of ICredentials.
from zope.interface import implementer, Interface


class ICredentials(Interface):
    pass


class IToken(ICredentials):
    pass


@implementer(IToken)
class Token:
    pass

Caching errors with latest version of mypy

Hi. With the latest version of mypy and mypy-zope we're getting mypy errors that I think are cache related. The error goes away after rm -rf .mypy_cache

Here's the smallest repro I could make:

src/zg/foo.py:

from zg.bar import ZGScriptProtocol

print(ZGScriptProtocol.transport)

src/zg/bar.py:

from twisted.internet.protocol import ProcessProtocol

class ZGScriptProtocol(ProcessProtocol):
    pass

I am able to reliably hit the problem by running the following commands:

$ rm -rf .mypy_cache
$ mypy src/zg/foo.py 
Success: no issues found in 1 source file
$ mypy src/zg/foo.py src/zg/bar.py 
src/zg/bar.py:3: error: Cannot determine consistent method resolution order (MRO) for "ZGScriptProtocol"
src/zg/foo.py:3: error: "Type[ZGScriptProtocol]" has no attribute "transport"
$ mypy --version
mypy 0.971 (compiled: yes)

mypy-zope==0.3.9 and twisted[tls]==22.4.0

make it possible to define a generic interface

This probably requires some implementation support from the zope.interface side, but I find myself wanting to be able to write this:

from dataclasses import dataclass
from typing import Generic, TypeVar

from zope.interface import Interface, implementer

T = TypeVar("T")

class IA(Generic[T], Interface):
    def m() -> T:
        ...

@implementer(IA)
@dataclass
class A(Generic[T]):
    _v: T
    def m(self) -> T:
        return self._v

a: IA[int] = A(3)

This actually appears to do what I want at type-check time! But then it (somewhat obviously) crashes at runtime.

This horrible hack almost works though, which suggests that this could work properly with some very small changes:

# from __future__ import annotations
from dataclasses import dataclass
from typing import Generic, TypeVar, TYPE_CHECKING

from zope.interface import Interface, implementer

T = TypeVar("T")

if TYPE_CHECKING:
    from typing import Generic as GenericInterface
else:
    from zope.interface.interface import InterfaceClass
    class SpecialInterfaceClass(InterfaceClass):
        def __getitem__(self, key):
            return self
    EmptyInterface = SpecialInterfaceClass("<Empty>", __module__=__name__)
    class GenericInterfaceClass:
        def __getitem__(self, typevars):
            return EmptyInterface
    GenericInterface = GenericInterfaceClass()

class IA(Interface, GenericInterface[T]):
    def m() -> T:
        ...

@implementer(IA)
@dataclass
class A(Generic[T]):
    _v: T
    def m(self) -> T:
        return self._v

@dataclass
class B:
    ...

a: IA[int] = A(3)               # works, hooray
a = B()                         # error, hooray
a = A("oops")                   # no error, boo

Clearer error message when "self" is used in method inside interface definition

When checking the following modified example:

class IAnimal(zope.interface.Interface):
    def say(self) -> None:
        pass

The following error message is output:

error: Name 'self' already defined (possibly by an import)

While the plugin is correct in flagging this use of self as an error, the message is a bit cryptic. It would be nice to have a specific error message for this problem: I imagine I'm not the only one making this mistake in their code.

Regression in upcoming mypy 0.990

Hi. I was testing the upcoming mypy release and it seems to be emitting new warning for interface definitions:

import zope.interface


class IZomething(zope.interface.Interface):
	def Query(a: str, b: str) -> list[int]:
		pass

gives:

$ mypy --config=mypy.ini int.py 
int.py:5: error: Missing return statement  [empty-body]
Found 1 error in 1 file (checked 1 source file)

Replacing the function body with ellipses silences the warning. Perhaps that should be the preferred way to define interfaces moving forward?

Return interface in functions getUtility, getAdapter...

Hi,

First of all, thank you for this amazing library. I'm using this library on some projects with extensive use of getUtility and getAdapter, and I was expecting that these functions would have as a return type the interface of provides argument. Right now we need to do add the interface explicitly u: IMyUtility = getUtility(IMyUtility) to get type validation.

from zope.interface import Interface
from zope.interface import implementer
from zope.component import getUtility


class IMyUtility(Interface):
    def foo(x: int):
        pass


@implementer(IMyUtility)
class MyUtility():
    def foo(self, x):
        return 2 * x


u = getUtility(IMyUtility)
# Mypy doesn't produce any error
u.foo("2")


u2: IMyUtility = getUtility(IMyUtility)
# Mypy validation works and provides this error:
#   error: Argument 1 to "foo" of "IMyUtility" has incompatible type "str"; expected "int"
u2.foo("2")

I tried adding def getUtility(self, provided: T, name: str = ...) -> T: ... (https://github.com/Shoobx/mypy-zope/blob/master/src/zope-stubs/interface/registry.pyi#L27) but I'm getting Type[IMyUtility] instead of IMyUtility.

Any idea on how to fix it? Does it make sense to add this to mypy-zope?

Thank you!

"Interface should be specified" when interface name is qualified

When checking the following code:

from twisted.internet import interfaces
from zope.interface import implementer

@implementer(interfaces.IPushProducer)
class MyProducer:
    pass

This error is reported:

error: Interface should be specified (should never happen)

It is possible to work around this by importing IPushProducer directly instead of importing its module.

Quick question: regarding stubgen

Hello,

Thanks for your work on adding typing on zope.interface. Although we haven't used your project directly, it's been a lot of help.

We've been trying to generate stubs for zope.interface, but we're having problems generating one for C extension _zope_interface_coptimizations.c, with the following traceback:

Traceback (most recent call last):
  File "/home/user/tmp/_env/bin/stubgen", line 10, in <module>
    sys.exit(main())
  File "mypy/stubgen.py", line 1173, in main
  File "mypy/stubgen.py", line 1084, in generate_stubs
  File "mypy/stubgenc.py", line 51, in generate_stub_for_c_module
  File "mypy/stubgenc.py", line 268, in generate_c_type_stub
  File "mypy/stubgenc.py", line 153, in generate_c_function_stub
TypeError: str object expected

It's crashing trying to generate from docstring, we were wondering how you were able to generate the stub file.

Thanks again for taking time to read this.

Please make a release with "Support future mypy"

I'd like to include mypy-zope in my project, but if I roll back mypy from 0.670 to 0.660 I would get a bunch of false positives. I know I can install the current development state directly from GitHub with pip, but I'm using Poetry to maintain virtualenvs for multiple Python versions and installing packages directly with pip means I'd have to manually do that for every virtualenv, plus I have to manually update them later as well.

So it would really help me if there was a mypy-zope release on PyPI that depends on mypy >= 0.660.

release a wheel

currently only sdists are available.

python -m pip install -U build twine
python -m build --wheel --sdist .
python -m twine upload dist/*

should do the right thing when you next deploy

A way to express "A type which implements these N interfaces"

Let's say I have two interfaces IA and IB. They're completely independent of each other. I implement both in some type C.

from zope.interface import Interface, implementer

class IA(Interface):
    def do_a(): ...

class IB(Interface):
    def do_b(): ...


@implementer(IA, IB)
class C:
    def do_a(): ...
    def do_b(): ...

I can assign a C instance to a variable marked against either interface.

a: IA = C  # ok
b: IB = C  # ok

I can also pass a C instance to a function requiring either interface.

def needs_IA(a: IA): ...
def needs_IB(b: IB): ...

needs_IA(c)  # ok
needs_IB(c)  # ok

Suppose I have another function which requires both interfaces.

def needs_IA_and_IB(arg): ...
needs_IA_and_IB(c)         # want this to be checked as ok
needs_IA_and_IB(object())  # want this to be checked as not okay

If I know that I'm only going to use Cs, I can mark arg: C and call it a day.
But this doesn't work if I have a second type D that implements both interfaces.

@implementer(IA, IB)
class D:
    def do_a(): ...
    def do_b(): ...
    
    
d = D()
needs_IA_and_IB(d)  # want this to also be okay

How should I annotate arg? What I'd like is a way to describe "a type that implements each of these interfaces". Is there a way to do this at present? There's no notion of an intersection type; maybe there'd need to be a new Type spelt Implements[IA, IB, ...] for this purpose?

Example

To give a concrete example: Twisted's HostnameEndpoint.__init__ expects a reactor argument which should be

provider of IReactorTCP, IReactorTime and either IReactorPluggableNameResolver or IReactorPluggableResolver.

I don't think that can be currently expressed in the type system. I'd want to spell it as something like

Implements[
    IReactorTCP,
    IReactorTime,
    Union[IReactorPluggableNameResolver, IReactorPluggableResolver]
]

The inner Union might make things harder; even being able to express one of the two choices like

Implements[IReactorTCP, IReactorTime, IReactorPluggableNameResolver]

would be helpful!


I don't know if this is something in the remit of mypy-zope as opposed to the zope project itself. I'm hopingthis is a good to ask fo suggestions or feedback though!

Caching error (Metaclass conflict) with mypy 0.991

Hi. With the latest version of mypy (0.991) and mypy-zope (0.3.11) we're getting mypy errors that I think are cache related. The error goes away after rm -rf .mypy_cache

Here's the easiest repro I could make:

iface.py

from zope.interface import Interface
from zope.interface import implementer

class ISomething(Interface):
    pass

class ISomething2(Interface):
    pass

@implementer(ISomething)
class OneInterface:
    pass

@implementer(ISomething, ISomething2)
class TwoInterfaces:
    pass

foo.py

from iface import OneInterface, TwoInterfaces

class Good(OneInterface):
    pass

class Bad(TwoInterfaces):
    pass

I am able to reliably hit the problem by running the following commands:

$ rm -rf .mypy_cache
$ mypy iface.py 
Success: no issues found in 1 source file
$ mypy foo.py 
foo.py:6: error: Metaclass conflict: the metaclass of a derived class must be a (non-strict) subclass of the metaclasses of all its bases  [misc]
Found 1 error in 1 file (checked 1 source file)

As you can tell this only happens when there are multiple interfaces.

plugin causes open() signature to change

Hi,

I ran across an interesting issue. The signature of open() unexpectedly became typing.IO[Any] instead of typing.TextIO when the plugin is enabled.

example.py:

reveal_type(open('str'))

plugin.ini:

[mypy]
plugins = mypy_zope:plugin

Here is small test:

$ more example.py 
reveal_type(open('str'))
$ more plugin.ini 
[mypy]
plugins = mypy_zope:plugin
$ mypy --config plugin.ini example.py 
example.py:1: note: Revealed type is 'typing.IO[Any]'
$ mypy example.py 
example.py:1: note: Revealed type is 'typing.TextIO'

I created a test-case here:

master...arnimarj:bug/open-signature-changes

Zope Plugin breaks contextmanager plugin

Here's an example:

from collections import Iterator
from contextlib import contextmanager
from typing import TypeVar, Generic

_T = TypeVar("_T")


class A(Generic[_T]):
    pass

@contextmanager
def m(x: _T) -> Iterator[A[_T]]:
    ...


with m(7) as x:
    reveal_type(x)
    print(x)

Without the plugin you get: note: Revealed type is 'bar.A*[builtins.int*]'
With the plugin you get: Revealed type is 'bar.A*[_T-1]'`

I tracked the issue down to get_function_hook doing a return analyze for all methods thus preventing the hooks at mypy.plugins.default.DefaultPlugin.get_function_hook from running.

This is the same as #16

Looking at python/mypy#8331 I tried figuring out how one could use lookup_fully_qualified and here's some code that works for some cases.

        info = self.lookup_fully_qualified(fullname)
        if (
            info and info.node and isinstance(info.node, SYMBOL_FUNCBASE_TYPES)
            and info.node.type and isinstance(info.node.type, CallableType)
            and info.node.type.ret_type and isinstance(info.node.type.ret_type, Instance)
        ):
            info.node = info.node.type.ret_type.type

        if (info
            and info.node
            and isinstance(info.node, TypeInfo)
                and info.node.mro
                and any(typeinfo.fullname in {"zope.interface.interface.Attribute", "zope.schema.fieldproperty.FieldProperty"}
               for typeinfo in self.lookup_fully_qualified(fullname).node.mro)):
            return analyze

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.