Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

gh-100690: Prevent prefix "called_" for attributes of safe mock objects#100691

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to ourterms of service andprivacy statement. We’ll occasionally send you account related emails.

Already on GitHub?Sign in to your account

Merged
cjw296 merged 6 commits intopython:mainfromcklein:disallow_mock_method_prefix
Jan 6, 2023

Conversation

@cklein
Copy link
Contributor

@ckleincklein commentedJan 2, 2023
edited by AlexWaygood
Loading

Prevent prefix "called_" for mocks in safe mode

@bedevere-bot
Copy link

Most changes to Pythonrequire a NEWS entry.

Please add it using theblurb_it web app or theblurb command-line tool.

@ckleincklein changed the titlePrevent prefix "called_" for attributes of safe mock objectsgh-100690: Prevent prefix "called_" for attributes of safe mock objectsJan 2, 2023
@ckleinckleinforce-pushed thedisallow_mock_method_prefix branch 2 times, most recently from25e26d8 to7bc8c46CompareJanuary 2, 2023 16:25
Copy link
Contributor

@cjw296cjw296 left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

I'm -0 on this change, feels like we're into the diminishing realms where someone out there is going to have a legitimate use case forcalled_name or some such and this then prevents them using a safe mock.

Who else is hitting this?

@ckleinckleinforce-pushed thedisallow_mock_method_prefix branch from7bc8c46 tob84bc75CompareJanuary 2, 2023 16:46
@cklein
Copy link
ContributorAuthor

@cjw296, the same applies to any of the forbidden prefixes. This could be solved by allowing those prefixes with a spec. Currently, even if the name appears in_mock_methods (https://github.com/python/cpython/blob/main/Lib/unittest/mock.py#L651 evaluates to true), an error will be raised. But I guess this could be a different and more fundamental discussion.

@cklein
Copy link
ContributorAuthor

cklein commentedJan 2, 2023
edited by AlexWaygood
Loading

I have been thinking a bit more about the whole problem. It seems the current behavior does not match the error message:

>>>classFoo:...defassert_bar(self):...pass...>>>from unittest.mockimport Mock>>> m= Mock(spec=Foo)>>> m.assert_bar()Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/opt/local/Library/Frameworks/Python.framework/Versions/3.10/lib/python3.10/unittest/mock.py", line 648, in __getattr__    raise AttributeError(AttributeError: 'assert_bar' is not a valid assertion. Use a spec for the mock if 'assert_bar' is meant to be an attribute.

The error message says "Use a spec for the mock if 'assert_bar' is meant to be an attribute.", but this example should pass according to the error message. There's a spec and the nameassert_bar should be supported.

Withcklein@37aa291 applied, it behaves like described:

>>>classFoo:...defassert_bar(self):...pass...>>>from unittest.mockimport Mock>>> m= Mock(spec=Foo)>>> m.assert_bar()<Mock name='mock.assert_bar()' id='4384202080'>>>> m.assert_frobnicated()Traceback (most recent call last):  File "<stdin>", line 1, in <module>  File "/Users/cklein/github/cklein/cpython/Lib/unittest/mock.py", line 652, in __getattr__    raise AttributeError("Mock object has no attribute %r" % name)AttributeError: Mock object has no attribute 'assert_frobnicated'

@merwok
Copy link
Member

The prefixes checked could be more specific likecalled_once_with,called_once,called_with to alleviate the concern about forbidding too much.

cklein reacted with thumbs up emoji

@cjw296
Copy link
Contributor

@cklein - I think you're right about this being a complicated issue, perhaps a PR isn't the best place to discuss it? Could you maybe close this PR an open an issue explaining your concerns and some possible ways forward?

@AlexWaygood
Copy link
Member

Could you maybe close this PR an open an issue explaining your concerns and some possible ways forward?

OP has already done so:

@cklein
Copy link
ContributorAuthor

cklein commentedJan 3, 2023
edited
Loading

I can also create another issue for my second branch about the spec not being properly checked, if that helps

Edit: Seehttps://discuss.python.org/t/check-for-assert-prefixes-doesnt-respect-spec/22388

… modeRaise an AttributeError when calling e.g. `mock_obj.called_once` instead of`mock_obj.assert_called_once`.
@ckleinckleinforce-pushed thedisallow_mock_method_prefix branch fromb84bc75 to154012fCompareJanuary 3, 2023 11:35
@cklein
Copy link
ContributorAuthor

I'm aware that the documentation is now off. I'd like to get some feedback on the change first, if that's OK



# gh-100690 Denylist for forbidden attribute names in safe mode
ATTRIB_DENY_LIST= {name.removeprefix("assert_")fornameindir(NonCallableMock)ifname.startswith("assert_")}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Much prefer this approach! 🎉

@cjw296
Copy link
Contributor

Approach is good, so once the docs are fixed up, should be good to land.
Would a backport release once that's done be helpful? (I mean, I'll probably crank the handle anyway, but thought I'd ask!)

@cklein
Copy link
ContributorAuthor

I'm struggling a bit with documentation. What about the following:

    * `unsafe`: By default, accessing any attribute whose name starts with      *assert*, *assret*, *asert*, *aseert*, or *assrt* raises an AttributeError.      Additionally, an AttributeError is raised when accessing      attributes that match the name of assertion method without the prefix      `assert_`, e.g. accessing `called_once` instead of `assert_called_once`.      Passing `unsafe=True` will allow access to these attributes.

@merwokmerwok added type-featureA feature request or enhancement stdlibStandard Library Python modules in the Lib/ directory labelsJan 5, 2023
@cklein
Copy link
ContributorAuthor

Would a backport release once that's done be helpful?

Personally I'd like to see it in earlier versions of Python.
But then I'm a bit torn because on the one hand this will definitely make some tests fail, on the other hand those tests will finally be properly executed and some unwanted behavior might be uncovered.

@cjw296
Copy link
Contributor

cjw296 commentedJan 6, 2023
edited
Loading

@cklein - 3.10 and 3.11 are open for bug fixes so their next third point release would contain this once its landed.
...and to be clear: I consider this a bug in that there are checks for these problems but they are insufficient.

cklein reacted with heart emoji

@cjw296cjw296 added needs backport to 3.10only security fixes needs backport to 3.11only security fixes type-bugAn unexpected behavior, bug, or error and removed DO-NOT-MERGE type-featureA feature request or enhancement labelsJan 6, 2023
@cjw296cjw296 removed DO-NOT-MERGE needs backport to 3.10only security fixes needs backport to 3.11only security fixes labelsJan 6, 2023
@cjw296
Copy link
Contributor

The Azure Pipelines failure seems unrelated.

@cjw296cjw296 merged commit1d4d677 intopython:mainJan 6, 2023
@AlexWaygood
Copy link
Member

The Azure Pipelines failure seems unrelated.

Yes, that failure happens intermittently on a lot of PRs

@bedevere-bot
Copy link

⚠️⚠️⚠️ Buildbot failure⚠️⚠️⚠️

Hi! The buildbotPPC64LE RHEL7 LTO 3.x has failed when building commit1d4d677.

What do you need to do:

  1. Don't panic.
  2. Checkthe buildbot page in the devguide if you don't know what the buildbots are or how they work.
  3. Go to the page of the buildbot that failed (https://buildbot.python.org/all/#builders/503/builds/2845) and take a look at the build logs.
  4. Check if the failure is related to this commit (1d4d677) or if it is a false positive.
  5. If the failure is related to this commit, please, reflect that on the issue and make a new Pull Request with a fix.

You can take a look at the buildbot page here:

https://buildbot.python.org/all/#builders/503/builds/2845

Failed tests:

  • test_nntplib

Failed subtests:

  • tearDownClass - test.test_nntplib.NetworkedNNTPTests
  • test_with_statement - test.test_nntplib.NetworkedNNTPTests.test_with_statement

Summary of the results of the build (if available):

== Tests result: FAILURE then FAILURE ==

408 tests OK.

10 slowest tests:

  • test_concurrent_futures: 2 min 46 sec
  • test_multiprocessing_spawn: 1 min 45 sec
  • test_multiprocessing_forkserver: 1 min 20 sec
  • test_multiprocessing_fork: 1 min 9 sec
  • test_tokenize: 1 min 7 sec
  • test_asyncio: 1 min 2 sec
  • test_logging: 54.8 sec
  • test_signal: 48.0 sec
  • test_venv: 42.2 sec
  • test_io: 38.8 sec

1 test failed:
test_nntplib

24 tests skipped:
test_check_c_globals test_devpoll test_gdb test_idle test_ioctl
test_kqueue test_launcher test_msilib test_peg_generator
test_perf_profiler test_smtpnet test_ssl test_startfile test_tcl
test_tix test_tkinter test_ttk test_ttk_textonly test_turtle
test_winconsoleio test_winreg test_winsound test_wmi
test_zipfile64

1 re-run test:
test_nntplib

Total duration: 7 min 7 sec

Click to see traceback logs
Traceback (most recent call last):  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/test/test_nntplib.py", line252, inwrapped    meth(self)  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/test/test_nntplib.py", line286, intest_with_statement    server=self.NNTP_CLASS(self.NNTP_HOST,**kwargs)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line341, in__init__self._base_init(readermode)  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line359, in_base_initself.getcapabilities()  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line421, ingetcapabilities    resp, caps=self.capabilities()^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line590, incapabilities    resp, lines=self._longcmdstring("CAPABILITIES")^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line557, in_longcmdstring    resp,list=self._getlongresp(file)^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line508, in_getlongresp    resp=self._getresp()^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line481, in_getresp    resp=self._getline()^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line469, in_getlineifnot line:raiseEOFError^^^^^^^^^^^^^^EOFErrorTraceback (most recent call last):  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/test/test_nntplib.py", line347, intearDownClasscls.server.quit()  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line933, inquit    resp=self._shortcmd('QUIT')^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line543, in_shortcmdreturnself._getresp()^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line481, in_getresp    resp=self._getline()^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/nntplib.py", line464, in_getline    line=self.file.readline(_MAXLINE+1)^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^  File"/home/buildbot/buildarea/3.x.cstratak-RHEL7-ppc64le.lto/build/Lib/socket.py", line707, inreadintoreturnself._sock.recv_into(b)^^^^^^^^^^^^^^^^^^^^^^^TimeoutError:timed out



# Denylist for forbidden attribute names in safe mode
ATTRIB_DENY_LIST= {name.removeprefix("assert_")fornameindir(NonCallableMock)ifname.startswith("assert_")}
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Sorry for being late to the party, but anyways :)

I think thatATTRIB_DENY_LIST is an implementation detail, so it should be_ATTRIB_DENY_LIST. And also, it is not suited for mutation, isn't it? So, it should be afrozenset instead :)

cklein reacted with thumbs up emoji
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

Since this PR is already merged, I've opened#100819 with this proposal. Feel free to close if it should be public and mutable!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others.Learn more.

It is already non-public by not being included in__all__, and the mutability is not really an issue.

Sign up for freeto join this conversation on GitHub. Already have an account?Sign in to comment

Reviewers

@merwokmerwokmerwok left review comments

@sobolevnsobolevnsobolevn left review comments

@cjw296cjw296cjw296 approved these changes

Assignees

No one assigned

Labels

stdlibStandard Library Python modules in the Lib/ directorytype-bugAn unexpected behavior, bug, or error

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

6 participants

@cklein@bedevere-bot@merwok@cjw296@AlexWaygood@sobolevn

[8]ページ先頭

©2009-2025 Movatter.jp