source: tailor/vcpx/source.py @ 467

Revision 467, 5.7 KB checked in by lele@…, 8 years ago (diff)

Renamed method, since it's one of those that subclasses should reimplement

Line 
1# -*- mode: python; coding: utf-8 -*-
2# :Progetto: vcpx -- Updatable VC working directory
3# :Creato:   mer 09 giu 2004 13:55:35 CEST
4# :Autore:   Lele Gaifax <lele@nautilus.homeip.net>
5# :Licenza:  GNU General Public License
6#
7
8"""
9Updatable sources are the simplest abstract wrappers around a working
10directory under some kind of version control system.
11"""
12
13__docformat__ = 'reStructuredText'
14
15CONFLICTS_PROMPT = """
16The changeset
17
18%s
19caused conflicts on the following files:
20
21 * %s
22
23Either abort the session with Ctrl-C, or manually correct the situation
24with a Ctrl-Z and a few "svn resolved". What would you like to do?
25"""
26
27class GetUpstreamChangesetsFailure(Exception):
28    "Failure getting upstream changes"
29
30    pass
31
32class ChangesetApplicationFailure(Exception):
33    "Failure applying upstream changes"
34
35    pass
36
37class InvocationError(Exception):
38    "Bad invocation, use --help for details"
39
40    pass
41
42class UpdatableSourceWorkingDir(object):
43    """
44    This is an abstract working dir able to follow an upstream
45    source of ``changesets``.
46
47    It has three main functionalities:
48
49    _getUpstreamChangesets
50        to query the upstream server about new changesets
51
52    applyUpstreamChangesets
53        to apply them to the working directory
54
55    checkoutUpstreamRevision
56        to extract a new copy of the sources, actually initializing
57        the mechanism.
58
59    Subclasses MUST override at least the _underscoredMethods.
60    """
61
62    def applyUpstreamChangesets(self, root, module, changesets, applyable=None,
63                                replay=None, applied=None, logger=None,
64                                delayed_commit=False):
65        """
66        Apply the collected upstream changes.
67
68        Loop over the collected changesets, doing whatever is needed
69        to apply each one to the working dir and if the changes do
70        not raise conflicts call the `replay` function to mirror the
71        changes on the target.
72
73        Return a tuple of two elements:
74
75        - the last applied changeset, if any
76        - the sequence (potentially empty!) of conflicts.
77        """
78
79        c = None
80        last = None
81        conflicts = []
82        for c in changesets:
83            if not self._willApplyChangeset(root, c, applyable):
84                continue
85
86            if logger:
87                logger.info("Applying changeset %s", c.revision)
88
89            try:
90                res = self._applyChangeset(root, c, logger=logger)
91            except:
92                if logger:
93                    logger.critical("Couldn't apply changeset %s",
94                                    c.revision, exc_info=True)
95                    logger.debug(str(c))
96                raise
97
98            if res:
99                conflicts.append((c, res))
100                try:
101                    raw_input(CONFLICTS_PROMPT % (str(c), '\n * '.join(res)))
102                except KeyboardInterrupt:
103                    if logger: logger.info("INTERRUPTED BY THE USER!")
104                    return last, conflicts
105
106            if replay:
107                replay(root, module, c, delayed_commit=delayed_commit,
108                       logger=logger)
109
110            if applied:
111                applied(root, c)
112
113            last = c
114
115        return last, conflicts
116
117    def _willApplyChangeset(self, root, changeset, applyable=None):
118        """
119        This gets called just before applying each changeset.  The action
120        won't be carried out if this returns False.
121
122        Subclasses may use this to skip some changeset, or to do whatever
123        before application.
124        """
125
126        if applyable:
127            return applyable(root, changeset)
128        else:
129            return True
130
131    def _getUpstreamChangesets(self, root, repository, module, sincerev):
132        """
133        Query the upstream repository about what happened on the
134        sources since last sync, returning a sequence of Changesets
135        instances.
136
137        This method must be overridden by subclasses.
138        """
139
140        raise "%s should override this method" % self.__class__
141
142    def _applyChangeset(self, root, changeset, logger=None):
143        """
144        Do the actual work of applying the changeset to the working copy.
145
146        Subclasses should reimplement this method performing the
147        necessary steps to *merge* given `changeset`, returning a list
148        with the conflicts, if any.
149        """
150
151        raise "%s should override this method" % self.__class__
152
153    def checkoutUpstreamRevision(self, root, repository, module, revision,
154                                 **kwargs):
155        """
156        Extract a working copy from a repository.
157
158        :root: the name of the directory (that **must** exists)
159               that will contain the working copy of the sources under the
160               *module* subdirectory
161
162        :repository: the address of the repository (the format depends on
163                     the actual method used by the subclass)
164
165        :module: the name of the module to extract
166
167        :revision: extract that revision/branch
168
169        Return the last applied changeset.
170        """
171
172        if not root:
173            raise InvocationError("Must specify a root directory")
174        if not repository:
175            raise InvocationError("Must specify an upstream repository")
176
177        return self._checkoutUpstreamRevision(root, repository,
178                                              module, revision,
179                                              **kwargs)
180
181    def _checkoutUpstreamRevision(self, basedir, repository, module, revision,
182                                  subdir=None, logger=None, **kwargs):
183        """
184        Concretely do the checkout of the upstream revision.
185        """
186
187        raise "%s should override this method" % self.__class__
Note: See TracBrowser for help on using the repository browser.