-
Notifications
You must be signed in to change notification settings - Fork 15
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 our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
100% coverage #30
100% coverage #30
Conversation
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Overall, I don't like using raise NotImplementedError()
as a replacement for pass
: it clutters the intent.
docs/testlayer.rst
Outdated
@@ -76,24 +76,29 @@ ZCMLLayer | |||
|
|||
We now want a layer that loads up some ZCML from a file. The default | |||
is ``ftesting.zcml``, but here we'll load a test ``testlayer.zcml``. | |||
We can also choose to provide extra ZCML features (here, "devmode"). |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This probably should either explain what devmode
does, or provide a link to the docs which do.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Are there such docs about "devmode"?
Maybe just a link to docs about features in general (if there are such things).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
There's documentation about features in general: http://zopeconfiguration.readthedocs.io/en/latest/narr.html#making-specific-directives-conditional
devmode
is a feature that enables additional debugging features for developer convenience, like Django's DEBUG = True
. E.g. in my Zope 3 projects I used hasFeature('devmode')
to decide whether to expose Python tracebacks to the end user in my SystemError views.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's what I wound up linking to:
We can also choose to provide extra ZCML features that are used
to conditionally control processing of certain directives <http://zopeconfiguration.readthedocs.io/en/latest/narr.html#making-specific-directives-conditional>
_
(here we use "devmode", a common condition for controlling development
options like debugging output).
import sys # pragma: no cover (runs in subprocess) | ||
import pickle # pragma: no cover (runs in subprocess) | ||
|
||
def write(x): # pragma: no cover (runs in subprocess) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ISTM it would be better just to exclude the whole module via .configrc
, e.g.:
[run]
omit =
src/zope/component/standalonetests.py
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
D'oh. I have no idea why I didn't do that.
@@ -54,8 +54,7 @@ def __init__(self, context): | |||
self.context = context | |||
|
|||
class Comp3(object): | |||
def __init__(self, context): | |||
self.context = context | |||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If the constructor is never exercised, then delete the whole class.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Don't know why it's not showing up as completed.
src/zope/component/tests/examples.py
Outdated
@@ -54,7 +54,7 @@ class ISII(Interface): | |||
pass | |||
|
|||
def noop(*args): | |||
pass | |||
pass # pragma: no cover |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete it if never called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Don't know why it's not showing as completed.
src/zope/component/tests/examples.py
Outdated
@@ -123,8 +123,7 @@ def __init__(self, context): | |||
|
|||
@implementer(I3) | |||
class Comp2(object): | |||
def __init__(self, context): | |||
self.context = context | |||
pass |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Delete the class if ctor is never called.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Done. Don't know why it's not showing as completed.
@@ -1092,7 +1083,7 @@ def __conform__(self, iface): | |||
def queryUtility(self, iface, name, default): | |||
if iface is IFactory and name == 'test': | |||
return _Factory() | |||
return default | |||
raise NotImplementedError() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
pragma
instead.
@@ -80,22 +80,11 @@ def _run_generated_code(self, code, globs, locs, | |||
fails_under_py3k=True, | |||
): | |||
import warnings | |||
#from zope.component._compat import PYTHON3 | |||
PYTHON3 = False |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This whole testcase class should be wrapped up in a skipIf(sys.version_info[0] > 2)
check.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I don't think that's actually necessary. The fails_under_py3k
argument can just be removed now...all the tests pass under both versions.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
The "class advice" directives cannot be used at all under Python3.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
That's true for the zope.interface
versions---they specifically blacklist against that. But zope.component.adapts
can be used in Python 3:
Python 3.5.3 (default, Apr 23 2017, 18:09:27)
[GCC 4.2.1 Compatible Apple LLVM 8.0.0 (clang-800.0.42.1)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from zope.component._declaration import adapts
>>> from zope.interface import Interface
>>> class Foo(object):
... adapts(Interface)
...
>>> from zope.component._declaration import adaptedBy
>>> adaptedBy(Foo)
(<InterfaceClass zope.interface.Interface>,)
Which is the same as under Python 2:
Python 2.7.13 (default, May 5 2017, 09:16:47)
[GCC 4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.38)] on darwin
Type "help", "copyright", "credits" or "license" for more information.
>>> from zope.component._declaration import adapts
>>> from zope.interface import Interface
>>> class Foo(object):
... adapts(Interface)
...
>>> from zope.component._declaration import adaptedBy
>>> adaptedBy(Foo)
(<InterfaceClass zope.interface.Interface>,)
So these tests all pass both places.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For my own reference, because I'd never looked into this before: The zope.interface
versions are broken because they want to modify the __metaclass__
variable of the caller, which is what doesn't work on Python 3. They do this in order to arrange to be run after the class has been constructed and hence its bases determined, so that they can "flatten" all of the implements declarations from the parent classes into the child class, presumably for performance.
z.c.adapts
doesn't have this issue, because it just simply assigns its argument to a value in the caller's locals. Parents aren't considered.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
OK. I still think we should be deprecating adapts
on the There should be one-- and preferably only one --obvious way to do it.
principle.
@@ -214,7 +219,7 @@ class Foo(object): | |||
pass | |||
@adapter(IFoo) | |||
def _handler(context): | |||
assert 0, "DON'T GO HERE" | |||
raise NotImplementedError() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
ISTM that the assert is clearer (with a pragma:
).
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Assertions can be turned off. It's not common under test code (I hope) but it is possible.
@@ -230,7 +235,7 @@ def test_w_adapts(self): | |||
class IFoo(Interface): | |||
pass | |||
def _handler(context): | |||
assert 0, "DON'T GO HERE" | |||
raise NotImplementedError() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ditto.
@@ -68,62 +68,38 @@ def bar(): | |||
def test_cant_assign_original(self): | |||
from zope.component.hookable import hookable | |||
def foo(): | |||
pass | |||
raise NotImplementedError() |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Wouldn't we be better off just adding pass
to the list of lines excluded by coverage.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
In my opinion, no.
I'll try to explain my thinking about the use of pass
vs NotImplementedError
in tests.
pass
is a legitimate thing to write with a specific meaning: This is a function, it should be called, although it doesn't actually do anything. If it's not called, coverage will go down, and something changed---there's probably a regression.
In contrast, raise NotImplementedError
has a different but still specific meaning: this is a callable, and I have to give it a name for reasons out of my control, but it must not be called. If it is called, something changed---there's probably a regression.
I see those two cases as useful distinctions to make.
I guess the "must not be called" case could usually be spelled "self.fail", although one would have to capture the correct "self" (or the method "self.fail"). Or we could introduce a specific exception class ("MustNotBeCalledError") to raise instead. Or we could have a pre-defined function that we simply assign to the needed name ("foo") in most cases: foo = callable_that_is_never_called
)...hey, that has the benefit of being a line shorter!
I wish there were a simple way to spell the pass
(must be called) case that would result in test failures instead of just coverage decreases.
Thoughts?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
@tseaver I updated the PR to use the foo = fails_if_called()
approach instead of NotImplementedError
. You're right, that does a better job capturing the intent and even reads easier, to me at least.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Random drive-by comment: to me raise AssertionError('should not be called')
seems like the best way to spell must-not-be-called, and raise NotImplementedError('please override in subclasses')
is the best way to spell abstract-method.
Add change note and badge to readme. Remove unused class and function. Omit standalonetests.py entirely from coverage. Another unused class. Incorporate feedback in test__api.py * Bring back _callFUT and make the queryAdapterInContext tests call it * Change raise NotImplentedError into specific fails_if_called() calls. Remove redundant argument now that all test in Test_adapts pass under all versions. Remove NotImplementedError from test_globalregistry.py Remove NotImplementedError from test_hookable.py Remove NotImplementedError from test_registry.py Remove NotImplementedError from test_security.py Remove NotImplementedError from test_zcml.py Remove NotImplementedError from test_factory.py Document ZCML feature and devmode. Really accept all arguments unless opted out.
Thanks everyone! |
Fixes #29.
Also simplify the "minimal" environment command by simply skipping tests that can't be run.