( )⚙ D332 repository: formalize peer interface with abstract base class

This is an archive of the discontinued Mercurial Phabricator instance.

repository: formalize peer interface with abstract base class
ClosedPublic

Authored by indygreg on Aug 11 2017, 12:20 AM.

Details

Summary

There are various interfaces for interacting with repositories
and peers. They form a contract for how one should interact with
a repo or peer object.

The contracts today aren't very well-defined or enforced. There
have been several bugs over the years where peers or repo types
have forgotten to implement certain methods. In addition, the
inheritance of some classes is wonky. For example, localrepository
doesn't inherit from an interface and the god-object nature of
that class means the repository interface isn't well-defined. Other
repository types inherit from localrepository then stub out
methods that don't make sense (e.g. statichttprepository
re-defining locking methods to fail fast).

Not having well-defined interfaces makes implementing alternate
storage backends, wire protocol transports, and repository types
difficult because it isn't clear what exactly needs to be
implemented.

This patch starts the process of attempting to establish more
order to the type system around repositories and peers.

Our first patch starts with a problem space that already has a
partial solution: peers. The peer.peerrepository class already
somewhat defines a peer interface. But it is missing a few things
and the total interface isn't well-defined because it is combined
with wireproto.wirepeer.

Our newly-established basepeer class uses the abc module to
declare an abstract base class with the properties and methods that
a generic peer must implement.

We create a new class that inherits from it. This class will hold
our other future abstract base classes / interfaces so we can expose
a unified base class/interface.

We don't yet use the new interface because subsequent additions
will break existing code without some refactoring first.

A new module (repository.py) was created to hold the interfaces.
I could have put things in peer.py. However, I have plans to
eventually add interfaces to define repository and storage types.
These almost certainly require a new module. And I figured having
all the interfaces live in one module makes sense. So I created
repository.py to be that future home.

Diff Detail

Repository
rHG Mercurial
Lint
Lint Skipped
Unit
Unit Tests Skipped

Event Timeline

indygreg created this revision.Aug 11 2017, 12:20 AM
durin42 added inline comments.
mercurial/repository.py
30

Nit: shouldn't this typically be a connection string with any credentials potentially redacted?

44

In what cases would a _basepeer not be a peer? What value does this part of the interface provide?

indygreg added inline comments.Aug 11 2017, 8:21 PM
mercurial/repository.py
44

TBH, I'm not sure why this exists. I cargo culted it from the existing peer API.

Once this series is accepted, I'd like to make some BC changes to the interface. This method is on the chopping block. Another is close(). AFAICT we don't call close() consistently and I'd like to switch things to context managers. But that's for another day...

indygreg added inline comments.Aug 13 2017, 1:55 PM
mercurial/repository.py
30

I looked at the code and today we're exposing the raw URL, before any redaction. This raw URL is then fed into things like hooks.

We should probably fix this by cleaning up existing usage and making the purpose of the attribute more well-defined. But I think it is outside the scope of this series. I'll add an inline TODO to track it.

indygreg updated this revision to Diff 847.Aug 13 2017, 2:18 PM
This revision was automatically updated to reflect the committed changes.