scmutil: explicitly subclass the `Status` protocol
We shouldn't have to explicitly subclass, but PyCharm has a nifty feature that
puts a jump point in the gutter to navigate back and forth between the base
class and subclasses (and override functions and base class functions) when
there's an explicit subclassing. Additionally, PyCharm will immediately flag
signature mismatches without a 40m pytype run.
It was also hoped that with explicit subclassing, we would get interface
checking for free. Unfortunately when I tried adding methods and fields to the
Protocol class to test this theory, pytype happily accepted an assignment of the
concrete class without the new field and methods, to a variable annotated with
the Protocol class with them. It appears that this is what happens when
explicit subclassing is used, since dropping that caused pytype to complain.
By making the methods abstract here like the `mercurial.wireprototypes` classes
in fd200f5bcaea, pytype will complain in that case outlined that a subclass with
abstract methods (not replaced by the subclass itself) cannot be instantiated.
That doesn't help with the fields. Making an `abstractproperty` likely isn't
appropriate in general, because that effectively becomes a read-only property.
This seems like a pretty gaping hole, but I think the benefits of explicit
subclassing are worth the risk. (Though I guess it shouldn't be surprising,
because a class can be both a Protocol and an implementation, so subclassing
something with an empty body method doesn't really signal that it is a
requirement for the subclass to implement.)
interfaces: add a Protocol class for `scmutil.status`
I initially tried moving this to the `interfaces` package, both to have more
cleanly defined interfaces (interfaces shouldn't have to reach into
implementation files for their type info), and because importing `mercurial.ui`
either directly or indirectly into `interfaces.repository` causes a situation
where pytype stops inferring the type for `revlogutils.constants` that are
imported by `revlog`. (Likely this is caused by a cycle. The `dirstate`
interface already imports `scmutil`, which in turn imports `ui`, so the
`repository` interface module importing the `dirstate` interface module as part
of converting those classes to Protocol classes will trigger the issue.)
I gave up on moving the class because `scmutil.status` depends on `stringutil`,
which has a surprisingly long tail of dependencies. In any event, a standalone
Protocol class might help with the Rust code.
interfaces: drop the conditional import of the vendored `zope` packages
The real `zope` code was only used when enabled by a test, and the decorators
turned into no-ops at runtime. Now that the test is disabled, unconditionally
use the no-op decorators and stop importing the code. This module can go away
once the `mercurial.interfaces.repository` interfaces are converted to Protocol
classes, but the vendored code can be deleted in the meantime.
interfaces: add the missing `self` arg to the repository Protocol classes
This clears all of the errors that PyCharm has been flagging in this file, since
the zope interfaces were declared here. This is the same transformation as
13aa17512583 did for dirstate.
interfaces: convert the repository zope interfaces to Protocol classes
This is the same transformation as 382d9629cede did for dirstate. The same
caveat applies- the code may not be valid, since the functions are missing the
`self` arg, and the attrs should be plain attrs, not zope `Attribute`. These
classes are pretty intertwined however, so making the same transformation to
everything makes it easier to change and review.
Additionally, there are some classes that subclass multiple protocol classes,
and should themselves subclass `typing.Protocol` to be a protocol class. But
defer that for now for clarity.
git: drop the zope `repository.ifilestorage` decoration on `gitlog.filelog`
The next logical step is to disable the conditional import of `zope` in the
`mercurial.interfaces.util` module, and just run with the no-op decorators.
But doing that then generates these pytype errors:
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 485, in read:
No attribute 'gitrepo' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 495, in lookup:
No attribute 'gitrepo' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 505, in add:
No attribute 'gitrepo' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 508, in __iter__:
No attribute '_db' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 522, in rev:
No attribute '_db' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 534, in node:
No attribute '_db' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 549, in parents:
No attribute '_db' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 556, in parents:
No attribute '_db' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 564, in parents:
No attribute 'gitrepo' on filelog [attribute-error]
File "/mnt/c/Users/Matt/hg/hgext/git/gitlog.py", line 564, in parents:
No attribute '_db' on filelog [attribute-error]
I'm not sure what exactly the issue is, but the pyi file that was being
generated up to this point has `filelog` typed as `Any` (because it had a
decorator, and pytype chokes on that), and `baselog` was typed as a class. So
it apparently can't see the `_db` and `gitrepo` attributes of the subclass,
because it doesn't think `filelog` is a class.
Leave the decorator commented out so it hits when searching for the remaining
zope things that need to be updated to Protocol.
tests: drop references to the vendored copy of `zope`
The `test-check-interfaces.py` test has mostly been a no-op since ef7d85089952.
Somehow, checks are still done on mere imports, as these errors were seen when
subclassing `Protocol` and adding the `self` argument to the repository
interfaces. So just get rid of it.
--- /builds/mercurial-ci/tests/test-check-interfaces.py.out
+++ /builds/mercurial-ci/tests/test-check-interfaces.py.err
@@ -0,0 +1,16 @@
+Traceback (most recent call last):
+ File "/builds/mercurial-ci/tests/test-check-interfaces.py", line 12, in <module>
+ from mercurial.interfaces import (
+ File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/interfaces/repository.py", line 401, in <module>
+ @interfaceutil.implementer(ipeerbase)
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^^
+ File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 388, in __call__
+ classImplements(ob, *self.interfaces)
+ File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 327, in classImplements
+ spec.declared += tuple(_normalizeargs(interfaces))
+ ^^^^^^^^^^^^^^^^^^^^^^^^^^
+ File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 910, in _normalizeargs
+ _normalizeargs(v, output)
+ File "/tmp/hgtests.hl7bqyl0/install/lib/python/mercurial/thirdparty/zope/interface/declarations.py", line 909, in _normalizeargs
+ for v in sequence:
+TypeError: '_ProtocolMeta' object is not iterable
ERROR: test-check-interfaces.py output changed
Additionally, as will be seen in the next commit, the fact that this code is
imported at all has an influence on pytype checking, even when it shouldn't be
getting used. Any replacement test will likely be a python file that
instantiates things and tries to assign them to variables annotated with a
Protocol, and is then checked with pytype. But in the meantime, the explicit
subclassing of the Protocol classes will give us some coverage.
ui: fix escape sequences in in readline prompts (issue6930)
Text that is meant to represent zero-width output in a readline
prompt, such as terminal escape sequences, is supposed to be
delimited by \001 ... \002:
> Applications may indicate that the prompt contains characters that
> take up no physical screen space when displayed by bracketing a
> sequence of such characters with the special markers
> RL_PROMPT_START_IGNORE and RL_PROMPT_END_IGNORE (declared in
> readline.h `\001' and `\002', respectively). This may be used to
> embed terminal-specific escape sequences in prompts.
https://tiswww.cwru.edu/php/chet/readline/readline.html#index-rl_005fexpand_005fprompt
When formatting a readline prompt in ui._readline, arrange to do this
in the color.py labelling routines. Keeping mutable dynamically
scoped state like this isn't great but threading it as a parameter
through all the subroutines seems like much more trouble.
(This doesn't address the missing line break -- that's a separate bug
in libedit.)
https://bz.mercurial-scm.org/show_bug.cgi?id=6930