Skip to content

Static analysis typing support for dynamic proxy classes

License

Notifications You must be signed in to change notification settings

obriencj/python-proxytype

Folders and files

NameName
Last commit message
Last commit date

Latest commit

 

History

31 Commits
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

Repository files navigation

Overview

preoccupied.proxytype is a Python package providing a MyPy plugin and a class decorator for triggering that plugin. The decorator can be used to indicate that a decorated class should be considered during static analysis to posess all of the methods from another class.

This pattern is distinct from inheritance, in that it will also apply a transformation to the return type of the copied methods. It is valid for this transformed return type to be incompatible with the original template class's method signature.

This is useful in situations where a dynamic class wraps or proxies its calls to another instance, optionally decorating them in the process.

The class decorator has no behavior at runtime -- it does not implement the proxying. It is only there to provide a way to declare such behavior during static analysis.

Installation

Install from the latest PyPI release

pip install --user preoccupied.proxytype

Install from a git checkout

make install

Enable plugin

At runtime the proxytype class decorator does not introduce a MyPy dependency. However in order for it to operate during MyPy's static analysis checks the plugin must be enabled. For example in setup.cfg

[mypy]
plugins =
  preoccupied.proxytype

Usage of proxytype

At runtime the proxytype class decorator has no impact. During static analysis via MyPy the proxytype class decorator can be used to virtually annotate that class with the methods from the originating type. Methods brought across to the decorated type will have their self argument type updated, and can optionally have their return type modified to be a wrapped generic around the original return type.

For example:

from preoccupied.proxytype import proxytype


class ClientSession:
    def doSomething(self, how_many: int, etc: Any) -> List[int]:
        ...

    def doAnother(self, wut: Any, **kwds: Any) -> bool:
        ...

    def getName(self) -> str:
        ...


RT = TypeVar("RT")

class DelayResult(Generic[RT]):
    resultValue: RT


@proxytype(ClientSession, DelayResult)
class DelayClientSession:
    def getName(self) -> str:
        ...

This will cause static analysys via MyPy of the DelayClientSession to appear as if it had been declared as:

class DelayClientSession:
    def doSomething(self, how_many: int, etc: Any) -> DelayResult[List[int]]:
        ...

    def doAnother(self, wut: Any, **kwds: Any) -> DelayResult[bool]:
        ...

    def getName(self) -> str:
        ...

Contact

author: Christopher O'Brien obriencj@preoccupied.net

original git repository: https://github.com/obriencj/python-proxytype

pypi project: https://pypi.org/project/preoccupied.proxytype

License

This library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 3 of the License, or (at your option) any later version.

This library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.

You should have received a copy of the GNU Lesser General Public License along with this library; if not, see http://www.gnu.org/licenses/.