[issue43478] Disallow Mock spec arguments from being Mocks

2022-02-02 Thread Matthew Suozzo


Change by Matthew Suozzo :


--
pull_requests: +29275
pull_request: https://github.com/python/cpython/pull/31090

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43478] Disallow Mock spec arguments from being Mocks

2021-03-11 Thread Matthew Suozzo


New submission from Matthew Suozzo :

An unfortunately common pattern over large codebases of Python tests is for 
spec'd Mock instances to be provided with Mock objects as their specs. This 
gives the false sense that a spec constraint is being applied when, in fact, 
nothing will be disallowed.

The two most frequently observed occurrences of this anti-pattern are as 
follows:

* Re-patching an existing autospec.

  def setUp(self):
mock.patch.object(mod, 'Klass', autospec=True).start()
self.addCleanup(mock.patch.stopall)

  @mock.patch.object(mod, 'Klass', autospec=True)  # :(
  def testFoo(self, mock_klass):
# mod.Klass has no effective spec.

* Deriving an autospec Mock from an already-mocked object

  def setUp(self):
mock.patch.object(mod, 'Klass').start()
...
mock_klass = mock.create_autospec(mod.Klass)  # :(
# mock_klass has no effective spec

This is fairly easy to detect using _is_instance_mock at patch time however it 
can break existing tests.

I have a patch ready and it seems like this error case is not frequent enough 
that it would be disruptive to address.

Another option would be add it as a warning for a version e.g. 3.10 and then 
potentially make it a breaking change in 3.11. However considering this is a 
test-only change with a fairly clear path to fix it, that might be overly 
cautious.

--
components: Tests
messages: 388532
nosy: msuozzo
priority: normal
severity: normal
status: open
title: Disallow Mock spec arguments from being Mocks
type: enhancement
versions: Python 3.10

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43478] Disallow Mock spec arguments from being Mocks

2021-03-15 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

I've fixed a bunch of these in our internal repo so I'd be happy to add that to 
a patch implementing raising exceptions for these cases.

--

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43478] Disallow Mock spec arguments from being Mocks

2021-03-16 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

A few more things:

Assertions on Mock-autospec'ed Mocks will silently pass since e.g. 
assert_called_once_with will now be mocked out. This may justify a more 
stringent stance on the pattern since it risks hiding real test failures.

One complicating factor with the implementation is that autospec'd objects may 
have children that are already Mocked out. Failing eagerly, however, would 
break cases where the child is never used (and consequently risk causing more 
friction than may be necessary) but failing only upon access of that child 
makes it trickier for the user to trace back to where the parent was mocked.

--

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43478] Disallow Mock spec arguments from being Mocks

2021-03-16 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

And to give some context for the above autospec child bit, this is the relevant 
code that determines the spec to use for each child: 
https://github.com/python/cpython/blob/master/Lib/unittest/mock.py#L2671-L2696

--

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43798] Add position metadata to alias AST type

2021-04-09 Thread Matthew Suozzo


New submission from Matthew Suozzo :

Given the increasing use of long `from typing import foo, bar, ...` import 
sequences, it's becoming more desirable to address individual components of the 
import node. Unfortunately, the ast.alias node doesn't contain source location 
metadata (e.g. lineno, col_offset).

This metadata would be comparatively easy to add, backwards compatible, and 
would enable more precise diagnostics e.g. lints.

--
components: Interpreter Core
messages: 390663
nosy: matthew.suozzo
priority: normal
severity: normal
status: open
title: Add position metadata to alias AST type
type: enhancement
versions: Python 3.10, Python 3.6, Python 3.7, Python 3.8, Python 3.9

___
Python tracker 
<https://bugs.python.org/issue43798>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43798] Add position metadata to alias AST type

2021-04-09 Thread Matthew Suozzo


Change by Matthew Suozzo :


--
keywords: +patch
pull_requests: +24059
stage:  -> patch review
pull_request: https://github.com/python/cpython/pull/25324

___
Python tracker 
<https://bugs.python.org/issue43798>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43798] Add position metadata to alias AST type

2021-04-09 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

Ah and one other question: Is this normally the sort of thing that would get 
backported? It should be very straightforward to do so, at least for 3.9 given 
the support for the new parser.

--
versions:  -Python 3.6, Python 3.7, Python 3.8

___
Python tracker 
<https://bugs.python.org/issue43798>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue43478] Disallow Mock spec arguments from being Mocks

2021-04-09 Thread Matthew Suozzo


Change by Matthew Suozzo :


--
keywords: +patch
nosy: +matthew.suozzo
nosy_count: 7.0 -> 8.0
pull_requests: +24061
stage:  -> patch review
pull_request: https://github.com/python/cpython/pull/25326

___
Python tracker 
<https://bugs.python.org/issue43478>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue37251] Mocking a MagicMock with a function spec results in an AsyncMock

2021-04-09 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

I don't think this was actually fixed for the create_autospec case. 
create_autospec still uses the only is_async_func check to enable use of 
AsyncMock and that still does a __code__ check.

There was a test submitted to check this case but the test itself was bugged 
and discovered in the process of implementing 
https://bugs.python.org/issue43478.

--
nosy: +matthew.suozzo

___
Python tracker 
<https://bugs.python.org/issue37251>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue37251] Mocking a MagicMock with a function spec results in an AsyncMock

2021-04-11 Thread Matthew Suozzo


Change by Matthew Suozzo :


--
pull_requests: +24081
stage: needs patch -> patch review
pull_request: https://github.com/python/cpython/pull/25347

___
Python tracker 
<https://bugs.python.org/issue37251>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue42551] Generator `yield`s counted as primitive calls by cProfile

2020-12-02 Thread Matthew Suozzo


New submission from Matthew Suozzo :

# Issue

When profiling a generator function, the initial call and all subsequent yields 
are aggregated into the same "ncalls" metric by cProfile.

## Example

>>> cProfile.run("""
... def foo():
...   yield 1
...   yield 2
... assert tuple(foo()) == (1, 2)
... """)
   
   ncalls  tottime  percall  cumtime  percall filename:lineno(function)
  ...
30.0000.0000.0000.000 :2(foo)
  ...

This was unexpected behavior since it *looks* like a single call from the code. 
This also complicates basic analysis about the frequency of a codepath's 
execution where a generator might yield a variable number of times depending on 
the input.

The profile interface can and does differentiate between call types: Normal 
calls and "primitive" calls, i.e. those that are not induced via recursion, are 
displayed as "all_calls/primitive_calls" e.g. "3/1" for a single initial calls 
with 2 recursed calls (comparable to the example above).

This seems like an appropriate abstraction to apply in the generator case: Each 
yield is better modeled as an 'interior' call to the generator, not as a call 
on its own.

# Possible fix

I have two ideas that seem like they might address the problem:

* Add a new PyTrace_YIELD constant (and handle it in [3])
* Track some state from the `frame->f_gen` in the ProfilerEntry (injected in 
[3], used in [4]) to determine whether this is the first or a subsequent call.

I've not been poking around for long so I don't have an intuition about which 
would be less invasive (nor really whether either would work in practice).

As background, this seems to be the call chain from trace invocation to 
callcount increment:
[0]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Python/ceval.c#L4106-L4107
[1]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Python/ceval.c#L4937
[2]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Python/ceval.c#L4961
[3]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Modules/_lsprof.c#L419
[4]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Modules/_lsprof.c#L389
[5]: 
https://github.com/python/cpython/blob/4e7a69bdb63a104587759d7784124492dcdd496e/Modules/_lsprof.c#L311-L316

--
components: C API
messages: 382373
nosy: matthew.suozzo
priority: normal
severity: normal
status: open
title: Generator `yield`s counted as primitive calls by cProfile
type: behavior
versions: Python 3.10, Python 3.6, Python 3.7, Python 3.8, Python 3.9

___
Python tracker 
<https://bugs.python.org/issue42551>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue42551] Generator `yield`s counted as primitive calls by cProfile

2020-12-14 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

One of the problems with my proposed solution that I glossed over was how and 
where to count the primitive call. If the primitive call is only registered on 
RETURN (i.e. after all yields), a generator that is incompletely exhausted 
would have 0 primitive calls. However if the primitive call is registered 
immediately when the call context is entered, the recorded call would be 
instantaneous (i.e. 0s duration) which could throw off the percall statistics.

Even with the incomplete generator case, I think registering the primitive call 
on RETURN is the best of the two options. Although seeing 2/0 for the call 
count when RETURN wasn't reached might seem odd, it lines up with a conceptual 
model of a coroutine: An iterator represents a function returning a sequence; 
Exhausting that iterator corresponds to the function call completing; 
Incompletely exhausting corresponds to an abbreviated/incomplete function call.

--

___
Python tracker 
<https://bugs.python.org/issue42551>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com



[issue42353] Proposal: re.prefixmatch method (alias for re.match)

2020-11-14 Thread Matthew Suozzo


Matthew Suozzo  added the comment:

> It just won't work unless you add explicit ".*" or ".*?" at the start of the 
> pattern

But think of when regexes are used for validating input. Getting it to "just 
work" may be over-permissive validation that only actually checks the beginning 
of the input. They're one missed test case away from a crash or, worse, a 
security issue.

This proposed name change would help make the function behavior obvious at the 
callsite. In the validator example, calling "prefixmatch" would stand out as 
wrong to even the most oblivious, documentation-averse user.

> My point is that re.match is a common bug when people really want re.search.

While I think better distinguishing the interfaces is a nice-to-have for 
usability, I think it has more absolute benefit to correctness. Again, 
confusion around the semantics of "match" were the motivation for adding 
"fullmatch" in the first place but that change only went so far to address the 
problem: It's still too easy to misuse the existing "match" interface and it's 
not realistic to remove it from the language. A new name would eliminate this 
class of error at a very low cost.

--
nosy: +matthew.suozzo

___
Python tracker 
<https://bugs.python.org/issue42353>
___
___
Python-bugs-list mailing list
Unsubscribe: 
https://mail.python.org/mailman/options/python-bugs-list/archive%40mail-archive.com