`unittest.mock.patch` and `unittest.mock.patch.object` are wrongly typed when new_callable is provided
Mantenedores costumam responder em até 1 dia
Ninguém assumiu esta issue ainda.
Avaliação
- Dificuldade
- 3/5
- Tempo estimado
- 1-2 dias
- Facilidade para iniciantes
- 48/100
Direção de pesquisa
Revise as sobrecargas de unittest.mock.patch e patch.object mostradas na issue e, em seguida, encontre seus stubs do typeshed e os testes associados. Verifique os tipos inferidos para patching padrão, new, new_callable e os casos de new_callable não chamável; a tarefa estará concluída quando ambas as APIs aceitarem apenas factories chamáveis e inferirem corretamente o tipo criado.
Escrita pelo modelo de indexação a partir do texto da issue.
Descrição
Steps to reproduce
from unittest.mock import patch
class NewPrinter:
pass
with patch("pprint.PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # "Union[unittest.mock.MagicMock, unittest.mock.AsyncMock]"
Same deal with unittest.mock.patch.object
from unittest.mock import patch
import pprint
class NewPrinter:
...
with patch.object(pprint, "PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # "Union[unittest.mock.MagicMock, unittest.mock.AsyncMock]"
Suggested fix
We discuss unittest.mock.patch in the following, but the same principles apply to unittest.mock.patch.object too
Current signatures:
@overload
def __call__(
self,
target: str,
new: _T,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Any | None = ...,
**kwargs: Any,
) -> _patch[_T]: ...
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Any | None = ...,
**kwargs: Any,
) -> _patch_default_new: ...
There are 2 problems here:
new_callableshouldn't beAny | Nonein the first place, it should only acceptCallable| None, as the it will raise if the provided value is not a callablewith patch("pprint.PrettyPrinter", new_callable=1) as patched_printer: # TypeError: 'int' object is not callable- Need to differentiate the case where new_callable is provided and left as default None
- When
new_callable: Callable[..., T], returns_patch[_T] - When
new_callable: None = ..., returns_patch_default_new
- When
Seems like this would do the job:
@overload
def __call__(
self,
target: str,
new: _T,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Callable[..., Any] | None = ...,
**kwargs: Any,
) -> _patch[_T]: ...
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: Callable[..., _T],
**kwargs: Any,
) -> _patch[_T]
@overload
def __call__(
self,
target: str,
*,
spec: Any | None = ...,
create: bool = ...,
spec_set: Any | None = ...,
autospec: Any | None = ...,
new_callable: None = ...,
**kwargs: Any,
) -> _patch_default_new: ...
Testing results
from unittest.mock import patch
class NewPrinter:
pass
with patch("pprint.PrettyPrinter") as patched_printer:
print(patched_printer) # <MagicMock name='PrettyPrinter' id='125908774402896'>
reveal_type(patched_printer) # MagicMock | AsyncMock
with patch("pprint.PrettyPrinter", new=NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # type[NewPrinter]
with patch("pprint.PrettyPrinter", new_callable=lambda: NewPrinter) as patched_printer:
print(patched_printer) # <class '__main__.NewPrinter'>
reveal_type(patched_printer) # type[NewPrinter]
# No overload variant matches for non-callable type
with patch("pprint.PrettyPrinter", new_callable="non-callable") as patched_printer:
...
Haven't tried unittest.mock.patch.object yet, but probably same thing
Would be great to see what the maintainers think.
I'll dig deeper and try to open a PR, maybe this weekend, will be my first PR in typeshed :)
- Linguagem predominante
- Python
- Estrelas
- 5.1k
- Forks
- 2.1k
- Merge médio
- 2d 1h
- PRs com merge (30d)
- 115
Preparar o ambiente
- Sem Dockerfile nem arquivo Docker Compose
- Sem modelo de pull request
- Ler o guia de contribuição
Primeiros passos
- Leia a issue inteira e depois o guia de contribuição do projeto.
- Comente na issue dizendo que vai assumir — evita que duas pessoas façam o mesmo trabalho.
- Faça um fork do repositório e trabalhe em uma branch.
- Abra um pull request que referencie o número da issue.
Mais de python/typeshed
-
`networkx.set_edge_attributes` should support scalar value + nameTalvez já em andamento @Fire162 assumiu há 9 dias. Aberta
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 67/100
Mantenedores costumam responder em até 1 dia
-
false alarm on decode callTalvez já em andamento @jw9829 assumiu há 37 dias. Abertahelp wanted stubs: false positive
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 76/100
python/typeshed#16441 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
Don't run pyright basic checks on projects with strict pyright settings in CITalvez já em andamento @Mohith1612 assumiu há 74 dias. Abertaproject: infrastructure
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 68/100
python/typeshed#16005 · 1 comentário ·
Mantenedores costumam responder em até 1 dia
-
stubs: improvement
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
python/typeshed#15548 · 5 comentários ·
Mantenedores costumam responder em até 1 dia
-
stubs: improvement
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 64/100
python/typeshed#15529 · 5 comentários ·
Mantenedores costumam responder em até 1 dia
Todas as issues de python/typeshed
Issues semelhantes
-
cost:cheap practicality:seen-in-the-wild severity:medium
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
fairagro/m4.2_advanced_middleware_api#620 ·
Mantenedores costumam responder em até 1 dia
-
area:security effort:S priority:P2 provider:local-libvirt status:ready type:docs
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 75/100
randomparity/kdive#3495 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 82/100
microsoft/markitdown#2820 ·
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 72/100
Mantenedores costumam responder em até 1 dia
-
Dificuldade 2/5 1-3 horas Facilidade para iniciantes 78/100
Mantenedores costumam responder em até 2 dias