#11610 closed enhancement (fixed)
Move Changeset, Node and Repository classes to model.py
Reported by: | Ryan J Ollos | Owned by: | Ryan J Ollos |
---|---|---|---|
Priority: | normal | Milestone: | 1.1.2 |
Component: | version control | Version: | |
Severity: | normal | Keywords: | |
Cc: | Jun Omae | Branch: | |
Release Notes: |
Git and Mercurial raise a |
||
API Changes: |
|
||
Internal Changes: |
Description
As discussed in #11609, move theChangeset
, Node
and Repository
classes to a new model.py
module in trac/versioncontrol
.
Attachments (2)
Change History (20)
comment:1 by , 11 years ago
Status: | new → assigned |
---|
comment:2 by , 11 years ago
comment:3 by , 11 years ago
Some additional experimental changes in log:rjollos.git:t11610
The classes in the abc
module were utilized in #11581. There are other classes in the repository which are made abstract by raising NotImplementedError
s in methods, but the ABCMeta
class forces subclasses to adhere to the contract (comment:2:ticket:11581), and more clearly identifies the base classes as abstract.
Some unit tests are currently failing due to:
GitRepository
doesn't implementedget_path_history
(#9775):====================================================================== ERROR: test_node_get_history_with_empty_commit (tracopt.versioncontrol.git.tests.PyGIT.NormalTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/user/Workspace/tracdev/teo-rjollos.git/tracopt/versioncontrol/git/tests/PyGIT.py", line 251, in test_node_get_history_with_empty_commit repos = self.env.get_repository('gitrepos') File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/env.py", line 567, in get_repository return RepositoryManager(self).get_repository(reponame) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/api.py", line 575, in get_repository repoinfo.copy()) File "/home/user/Workspace/tracdev/teo-rjollos.git/tracopt/versioncontrol/git/git_fs.py", line 280, in get_repository use_committer_time=self.use_committer_time, TypeError: Can't instantiate abstract class GitRepository with abstract methods get_path_history
- A
Mock
object can't be created directly from a base class withabstractmethods
. It is possible that we need to modifyMock
in order to deal with abstract base classes. I'm not sure yet what is the best approach to take on this.====================================================================== ERROR: test_clean_sync (trac.versioncontrol.tests.cache.CacheTestCase) ---------------------------------------------------------------------- Traceback (most recent call last): File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/tests/cache.py", line 158, in test_clean_sync youngest_rev=2) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/tests/cache.py", line 54, in get_repos next_rev=(lambda x: int(x) < youngest_rev and x + 1 \ File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/test.py", line 94, in Mock mock = cls(*initargs) TypeError: Can't instantiate abstract class Mock with abstract methods close, get_changes, get_changeset, get_node, get_oldest_rev, get_path_history, get_youngest_rev, next_rev, normalize_path, normalize_rev, previous_rev, rev_older_than
follow-up: 5 comment:4 by , 11 years ago
Changeset
is instantiated directly in CachedRepository. I fixed the failing test by instead instantiating the derived class. The only place I see the object used is: tags/trac-1.0.1/trac/versioncontrol/api.py@:662,674#L657. So now a GitChangeset
, SubversionChangeset
or other derived type is passed to the change listener. Are there any possible negative consequences to this that I'm not seeing?
As for creating Mock
instances of ABCs, it seemed most straightforward to just remove the abstract methods from the class. Like any other methods in a mocked object, an AttributeError
will be raised if methods omitted from the Mock
are utilized in a function under test.
Latest changes added to log:rjollos.git:t11610.
comment:5 by , 11 years ago
Cc: | added |
---|
Changeset
is instantiated directly in CachedRepository. I fixed the failing test by instead instantiating the derived class. The only place I see the object used is: tags/trac-1.0.1/trac/versioncontrol/api.py@:662,674#L657. So now aGitChangeset
,SubversionChangeset
or other derived type is passed to the change listener. Are there any possible negative consequences to this that I'm not seeing?
I think it would be good to create CachedChangeset
instance rather than Changeset
. Also, sync_changeset
method is for only cached repository. Then, GitChangeset
and SubversionChangeset
never be passed to the change listener.
See [058936ed/jomae.git].
As for creating
Mock
instances of ABCs, it seemed most straightforward to just remove the abstract methods from the class. Like any other methods in a mocked object, anAttributeError
will be raised if methods omitted from theMock
are utilized in a function under test.
We could add support for ABCMeta
class as base class of Mock. See [8f55ca19/jomae.git].
comment:6 by , 11 years ago
Those changes look good, thanks. Committed to trunk in [12782:12783].
We'll need to implement get_path_history
in the MercurialPlugin's Repository
subclass. I don't have the necessary permission to push to the branch.
-
tracext/hg/backend.py
# HG changeset patch # User ryan.j.ollos # Date 1400115750 25200 # Branch 1.0 # Node ID 251cde2ad7fdddbfbde7542bccd2a2f4a129b9c9 # Parent 21bb3cc19977ee1f5e546fc21f440ebfd6231404 Implemented abstract method `get_path_history`, which currently displays just a `TracError`. Refs #11610, #9775. diff -r 21bb3cc19977 -r 251cde2ad7fd tracext/hg/backend.py
a b 33 33 from trac.util import arity 34 34 from trac.util.datefmt import FixedOffset, utc 35 35 from trac.util.text import exception_to_unicode, shorten_line, to_unicode 36 from trac.util.translation import domain_functions36 from trac.util.translation import _, domain_functions 37 37 from trac.versioncontrol.api import Changeset, Node, Repository, \ 38 38 IRepositoryConnector, RepositoryManager, \ 39 39 NoSuchChangeset, NoSuchNode … … 747 747 def get_youngest_rev(self): 748 748 return self.changectx().hex() 749 749 750 def get_path_history(self, path, rev=None, limit=None): 751 raise TracError(_("Unsupported \"Show only adds and deletes\"")) 752 750 753 def previous_rev(self, rev, path=''): # FIXME: path ignored for now 751 754 for p in self.changectx(rev).parents(): 752 755 if p:
follow-ups: 8 11 comment:7 by , 10 years ago
Change looks fine, you can push it (I've added the perm).
However, the failure of one plugin shouldn't bring the whole BrowserModule
down (as can be seen on the demo-1.1 instance today):
2014-05-21 10:16:37,089 Trac[chrome] ERROR: Error with navigation contributor BrowserModule: TypeError: Can't instantiate abstract class MercurialRepository with abstract methods get_path_history
by , 10 years ago
Attachment: | ReposBrowser1.png added |
---|
by , 10 years ago
Attachment: | ReposBrowser2.png added |
---|
comment:8 by , 10 years ago
Replying to cboos:
However, the failure of one plugin shouldn't bring the whole
BrowserModule
down (as can be seen on the demo-1.1 instance today):
We could trap the TypeError
in RepositoryManager.get_repository
:
-
trac/versioncontrol/api.py
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py index 9a26e34..76baef1 100644
a b class RepositoryManager(Component): 571 571 if not os.path.isabs(rdir): 572 572 rdir = os.path.join(self.env.path, rdir) 573 573 connector = self._get_connector(rtype) 574 repos = connector.get_repository(rtype, rdir, 575 repoinfo.copy()) 576 repositories[reponame] = repos 574 try: 575 repos = connector.get_repository(rtype, rdir, 576 repoinfo.copy()) 577 except TypeError as e: # Incomplete implementation of 578 self.log.error(e) # abstract class 579 repos = None 580 else: 581 repositories[reponame] = repos 577 582 return repos 578 583 579 584 def get_repository_by_path(self, path):
In the repository browser we'd see:
In the log we'd see:
02:15:45 AM Trac[api] ERROR: Can't instantiate abstract class MercurialRepository with abstract methods get_path_history
follow-up: 10 comment:9 by , 10 years ago
Should we revert the change for Repository.get_path_history
to keep backward-compatibilities?
-
trac/versioncontrol/api.py
diff --git a/trac/versioncontrol/api.py b/trac/versioncontrol/api.py index 9a26e34..61bc40f 100644
a b class Repository(object): 944 944 """ 945 945 pass 946 946 947 @abstractmethod948 947 def get_path_history(self, path, rev=None, limit=None): 949 948 """Retrieve all the revisions containing this path. 950 949 … … class Repository(object): 952 951 ''newer'' than `rev` should be returned). 953 952 The result format should be the same as the one of Node.get_history() 954 953 """ 955 pass954 raise NotImplementedError 956 955 957 956 @abstractmethod 958 957 def normalize_path(self, path):
comment:10 by , 10 years ago
Replying to jomae:
Should we revert the change for
Repository.get_path_history
to keep backward-compatibilities?
That would defeat the purpose of the abstract interface. MercurialPlugin is wrong for not implementing that method, and now we are enforcing it in on the trunk, forcing MercurialPlugin to properly implement the interface of the Repository
class. We are not changing the interface, we are just enforcing it. MercurialPlugin can implement the method and be compatible with all versions of Trac, thus we aren't putting any burden on the maintainer of the MercurialPlugin. I don't think it is burdensome to force the upgrade of plugins when moving to a new major Trac version.
comment:11 by , 10 years ago
comment:12 by , 10 years ago
Resolution: | → fixed |
---|---|
Status: | assigned → closed |
Change to MercurialPlugin committed in [96c854b89f5c/mercurial-plugin]. The Repository.get_path_history
method won't be made abstract until Trac 1.3.1, in order to give time for Trac instances to update VC plugins that may not implement this method in earlier versions (such as MercurialPlugin): [12832].
comment:13 by , 10 years ago
I encountered the error when navigating to a repository: TypeError: Can't instantiate abstract class Changeset with abstract methods get_changes:
File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/web/main.py", line 530, in _dispatch_request dispatcher.dispatch(req) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/web/main.py", line 223, in dispatch resp = chosen_handler.process_request(req) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/web_ui/browser.py", line 406, in process_request dir_data = self._render_dir(req, repos, node, rev, order, desc) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/web_ui/browser.py", line 581, in _render_dir self.log) File "/home/user/Workspace/tracdev/teo-rjollos.git/trac/versioncontrol/web_ui/util.py", line 45, in get_changes datetime(1970, 1, 1, tzinfo=utc))
Either Changeset
will need to be non-abstract, or some other solution will need to be found for the usage in trunk/trac/versioncontrol/web_ui/util.py@12888:44-45#L36.
follow-up: 15 comment:14 by , 10 years ago
That is introduced in [10116], see comment:4:ticket:7800.
How about adding pseudo class for dummy changesets like this?
class PseudoChangeset(Changeset): # or named `NullChangeset` def get_changes(self): return iter([])
follow-up: 16 comment:15 by , 10 years ago
Replying to jomae:
How about adding pseudo class for dummy changesets like this?
That sounds good. How does EmptyChangeset
sound? NullChangeset
seems okay as well.
comment:16 by , 10 years ago
comment:18 by , 10 years ago
API Changes: | modified (diff) |
---|
Change discussed in comment:13 - comment:17 committed to trunk in [12983:12984].
Looking it at it more closely now, it could be that the reason the classes are in
api.py
rather thanmodel.py
is that they are abstract classes.