"Fossies" - the Fresh Open Source Software Archive

Member "viewvc-1.2.1/lib/vclib/__init__.py" (26 Mar 2020, 14339 Bytes) of package /linux/misc/viewvc-1.2.1.tar.gz:


As a special service "Fossies" has tried to format the requested source page into HTML format using (guessed) Python source code syntax highlighting (style: standard) with prefixed line numbers. Alternatively you can here view or download the uninterpreted source code file. For more information about "__init__.py" see the Fossies "Dox" file reference documentation and the latest Fossies "Diffs" side-by-side code changes report: 1.1.28_vs_1.2.1.

    1 # -*-python-*-
    2 #
    3 # Copyright (C) 1999-2020 The ViewCVS Group. All Rights Reserved.
    4 #
    5 # By using this file, you agree to the terms and conditions set forth in
    6 # the LICENSE.html file which can be found at the top level of the ViewVC
    7 # distribution or at http://viewvc.org/license-1.html.
    8 #
    9 # For more information, visit http://viewvc.org/
   10 #
   11 # -----------------------------------------------------------------------
   12 
   13 """Version Control lib is an abstract API to access versioning systems
   14 such as CVS.
   15 """
   16 
   17 import types
   18 
   19 
   20 # item types returned by Repository.itemtype().
   21 FILE = 'FILE'
   22 DIR = 'DIR'
   23 
   24 # diff types recognized by Repository.rawdiff().
   25 UNIFIED = 1
   26 CONTEXT = 2
   27 SIDE_BY_SIDE = 3
   28 
   29 # root types returned by Repository.roottype().
   30 CVS = 'cvs'
   31 SVN = 'svn'
   32 
   33 # action kinds found in ChangedPath.action
   34 ADDED      = 'added'
   35 DELETED    = 'deleted'
   36 REPLACED   = 'replaced'
   37 MODIFIED   = 'modified'
   38 
   39 # log sort keys
   40 SORTBY_DEFAULT = 0  # default/no sorting
   41 SORTBY_DATE    = 1  # sorted by date, youngest first
   42 SORTBY_REV     = 2  # sorted by revision, youngest first
   43 
   44 
   45 # ======================================================================
   46 #
   47 class Repository:
   48   """Abstract class representing a repository."""
   49 
   50   def rootname(self):
   51     """Return the name of this repository."""
   52 
   53   def roottype(self):
   54     """Return the type of this repository (vclib.CVS, vclib.SVN, ...)."""
   55 
   56   def rootpath(self):
   57     """Return the location of this repository."""
   58 
   59   def authorizer(self):
   60     """Return the vcauth.Authorizer object associated with this
   61     repository, or None if no such association has been made."""
   62     
   63   def open(self):
   64     """Open a connection to the repository."""
   65     
   66   def itemtype(self, path_parts, rev):
   67     """Return the type of the item (file or dir) at the given path and revision
   68 
   69     The result will be vclib.DIR or vclib.FILE
   70 
   71     The path is specified as a list of components, relative to the root
   72     of the repository. e.g. ["subdir1", "subdir2", "filename"]
   73 
   74     rev is the revision of the item to check
   75     """
   76     pass
   77 
   78   def openfile(self, path_parts, rev, options):
   79     """Open a file object to read file contents at a given path and revision.
   80 
   81     The return value is a 2-tuple of containg the file object and revision
   82     number in canonical form.
   83 
   84     The path is specified as a list of components, relative to the root
   85     of the repository. e.g. ["subdir1", "subdir2", "filename"]
   86 
   87     rev is the revision of the file to check out
   88 
   89     options is a dictionary of implementation specific options
   90     """
   91 
   92   def listdir(self, path_parts, rev, options):
   93     """Return list of files in a directory
   94 
   95     The result is a list of DirEntry objects
   96 
   97     The path is specified as a list of components, relative to the root
   98     of the repository. e.g. ["subdir1", "subdir2", "filename"]
   99 
  100     rev is the revision of the directory to list
  101 
  102     options is a dictionary of implementation specific options
  103     """
  104 
  105   def dirlogs(self, path_parts, rev, entries, options):
  106     """Augment directory entries with log information
  107 
  108     New properties will be set on all of the DirEntry objects in the entries
  109     list. At the very least, a "rev" property will be set to a revision
  110     number or None if the entry doesn't have a number. Other properties that
  111     may be set include "date", "author", "log", "size", and "lockinfo".
  112 
  113     The path is specified as a list of components, relative to the root
  114     of the repository. e.g. ["subdir1", "subdir2", "filename"]
  115 
  116     rev is the revision of the directory listing and will effect which log
  117     messages are returned
  118 
  119     entries is a list of DirEntry objects returned from a previous call to
  120     the listdir() method
  121 
  122     options is a dictionary of implementation specific options
  123     """
  124   
  125   def itemlog(self, path_parts, rev, sortby, first, limit, options):
  126     """Retrieve an item's log information
  127 
  128     The result is a list of Revision objects
  129 
  130     The path is specified as a list of components, relative to the root
  131     of the repository. e.g. ["subdir1", "subdir2", "filename"]
  132 
  133     rev is the revision of the item to return information about
  134 
  135     sortby indicates the way in which the returned list should be
  136     sorted (SORTBY_DEFAULT, SORTBY_DATE, SORTBY_REV)
  137 
  138     first is the 0-based index of the first Revision returned (after
  139     sorting, if any, has occured)
  140 
  141     limit is the maximum number of returned Revisions, or 0 to return
  142     all available data
  143     
  144     options is a dictionary of implementation specific options
  145     """
  146 
  147   def itemprops(self, path_parts, rev):
  148     """Return a dictionary mapping property names to property values
  149     for properties stored on an item.
  150 
  151     The path is specified as a list of components, relative to the root
  152     of the repository. e.g. ["subdir1", "subdir2", "filename"]
  153 
  154     rev is the revision of the item to return information about.
  155     """
  156     
  157   def rawdiff(self, path_parts1, rev1, path_parts2, rev2, type, options={}):
  158     """Return a diff (in GNU diff format) of two file revisions
  159 
  160     type is the requested diff type (UNIFIED, CONTEXT, etc)
  161 
  162     options is a dictionary that can contain the following options plus
  163     implementation-specific options
  164 
  165       context - integer, number of context lines to include
  166       funout - boolean, include C function names
  167       ignore_white - boolean, ignore whitespace
  168 
  169     Return value is a python file object
  170     """
  171 
  172   def annotate(self, path_parts, rev, include_text=False):
  173     """Return a list of Annotation object, sorted by their
  174     "line_number" components, which describe the lines of given
  175     version of a file.
  176 
  177     The file path is specified as a list of components, relative to
  178     the root of the repository. e.g. ["subdir1", "subdir2", "filename"]
  179 
  180     rev is the revision of the item to return information about.
  181     
  182     If include_text is true, populate the Annotation objects' "text"
  183     members with the corresponding line of file content; otherwise,
  184     leave that member set to None."""
  185 
  186   def revinfo(self, rev):
  187     """Return information about a global revision
  188 
  189     rev is the revision of the item to return information about
  190     
  191     Return value is a 5-tuple containing: the date, author, log
  192     message, a list of ChangedPath items representing paths changed,
  193     and a dictionary mapping property names to property values for
  194     properties stored on an item.
  195 
  196     Raise vclib.UnsupportedFeature if the version control system
  197     doesn't support a global revision concept.
  198     """
  199 
  200   def isexecutable(self, path_parts, rev):
  201     """Return true iff a given revision of a versioned file is to be
  202     considered an executable program or script.
  203 
  204     The path is specified as a list of components, relative to the root
  205     of the repository. e.g. ["subdir1", "subdir2", "filename"]
  206 
  207     rev is the revision of the item to return information about
  208     """
  209 
  210   def filesize(self, path_parts, rev):
  211     """Return the size of a versioned file's contents if it can be
  212     obtained without a brute force measurement; -1 otherwise.
  213 
  214     NOTE: Callers that require a filesize answer when this function
  215     returns -1 may obtain it by measuring the data returned via
  216     openfile().
  217     
  218     The path is specified as a list of components, relative to the root
  219     of the repository. e.g. ["subdir1", "subdir2", "filename"]
  220 
  221     rev is the revision of the item to return information about
  222     """
  223 
  224     
  225 # ======================================================================
  226 class DirEntry:
  227   """Instances represent items in a directory listing"""
  228 
  229   def __init__(self, name, kind, errors=[]):
  230     """Create a new DirEntry() item:
  231           NAME:  The name of the directory entry
  232           KIND:  The path kind of the entry (vclib.DIR, vclib.FILE)
  233           ERRORS:  A list of error strings representing problems encountered
  234                    while determining the other info about this entry
  235     """
  236     self.name = name
  237     self.kind = kind
  238     self.errors = errors
  239 
  240 class Revision:
  241   """Instances holds information about revisions of versioned resources"""
  242 
  243   def __init__(self, number, string, date, author, changed, log, size, lockinfo):
  244     """Create a new Revision() item:
  245           NUMBER:  Revision in an integer-based, sortable format
  246           STRING:  Revision as a string
  247           DATE:  Seconds since Epoch (GMT) that this revision was created
  248           AUTHOR:  Author of the revision
  249           CHANGED:  Lines-changed (contextual diff) information
  250           LOG:  Log message associated with the creation of this revision
  251           SIZE:  Size (in bytes) of this revision's fulltext (files only)
  252           LOCKINFO:  Information about locks held on this revision
  253     """
  254     self.number = number
  255     self.string = string
  256     self.date = date
  257     self.author = author
  258     self.changed = changed
  259     self.log = log
  260     self.size = size
  261     self.lockinfo = lockinfo
  262 
  263   def __cmp__(self, other):
  264     return cmp(self.number, other.number)
  265 
  266 class Annotation:
  267   """Instances represent per-line file annotation information"""
  268 
  269   def __init__(self, text, line_number, rev, prev_rev, author, date):
  270     """Create a new Annotation() item:
  271           TEXT:  Raw text of a line of file contents
  272           LINE_NUMBER:  Line number on which the line is found
  273           REV:  Revision in which the line was last modified
  274           PREV_REV:  Revision prior to 'rev'
  275           AUTHOR:  Author who last modified the line
  276           DATE:  Date on which the line was last modified, in seconds since
  277                  the epoch, GMT
  278     """
  279     self.text = text
  280     self.line_number = line_number
  281     self.rev = rev
  282     self.prev_rev = prev_rev
  283     self.author = author
  284     self.date = date
  285 
  286 class ChangedPath:
  287   """Instances represent changes to paths"""
  288 
  289   def __init__(self, path_parts, rev, pathtype, base_path_parts,
  290                base_rev, action, copied, text_changed, props_changed):
  291     """Create a new ChangedPath() item:
  292           PATH_PARTS:       Path that was changed
  293           REV:              Revision represented by this change
  294           PATHTYPE:         Type of this path (vclib.DIR, vclib.FILE, ...)
  295           BASE_PATH_PARTS:  Previous path for this changed item
  296           BASE_REV:         Previous revision for this changed item
  297           ACTION:           Kind of change (vclib.ADDED, vclib.DELETED, ...)
  298           COPIED:           Boolean -- was this path copied from elsewhere?
  299           TEXT_CHANGED:     Boolean -- did the file's text change?
  300           PROPS_CHANGED:    Boolean -- did the item's metadata change?
  301     """
  302     self.path_parts = path_parts
  303     self.rev = rev
  304     self.pathtype = pathtype
  305     self.base_path_parts = base_path_parts
  306     self.base_rev = base_rev
  307     self.action = action
  308     self.copied = copied
  309     self.text_changed = text_changed
  310     self.props_changed = props_changed
  311 
  312 
  313 # ======================================================================
  314 
  315 class Error(Exception):
  316   pass
  317 
  318 class ReposNotFound(Error):
  319   pass
  320 
  321 class UnsupportedFeature(Error):
  322   pass
  323 
  324 class ItemNotFound(Error):
  325   def __init__(self, path):
  326     # use '/' rather than os.sep because this is for user consumption, and
  327     # it was defined using URL separators
  328     if type(path) in (types.TupleType, types.ListType):
  329       path = '/'.join(path)
  330     Error.__init__(self, path)
  331 
  332 class InvalidRevision(Error):
  333   def __init__(self, revision=None):
  334     if revision is None:
  335       Error.__init__(self, "Invalid revision")
  336     else:
  337       Error.__init__(self, "Invalid revision " + str(revision))
  338 
  339 class NonTextualFileContents(Error):
  340   pass
  341 
  342 # ======================================================================
  343 # Implementation code used by multiple vclib modules
  344 
  345 import popen
  346 import os
  347 import time
  348 
  349 def _diff_args(type, options):
  350   """generate argument list to pass to diff or rcsdiff"""
  351   args = []
  352   if type == CONTEXT:
  353     if options.has_key('context'):
  354       if options['context'] is None:
  355         args.append('--context=-1')
  356       else:
  357         args.append('--context=%i' % options['context'])
  358     else:
  359       args.append('-c')
  360   elif type == UNIFIED:
  361     if options.has_key('context'):
  362       if options['context'] is None:
  363         args.append('--unified=-1')
  364       else:
  365         args.append('--unified=%i' % options['context'])
  366     else:
  367       args.append('-u')
  368   elif type == SIDE_BY_SIDE:
  369     args.append('--side-by-side')
  370     args.append('--width=164')
  371   else:
  372     raise NotImplementedError
  373 
  374   if options.get('funout', 0):
  375     args.append('-p')
  376 
  377   if options.get('ignore_white', 0):
  378     args.append('-w')
  379 
  380   return args
  381 
  382 class _diff_fp:
  383   """File object reading a diff between temporary files, cleaning up
  384   on close"""
  385 
  386   def __init__(self, temp1, temp2, info1=None, info2=None, diff_cmd='diff', diff_opts=[]):
  387     self.temp1 = temp1
  388     self.temp2 = temp2
  389     args = diff_opts[:]
  390     if info1 and info2:
  391       args.extend(["-L", self._label(info1), "-L", self._label(info2)])
  392     args.extend([temp1, temp2])
  393     self.fp = popen.popen(diff_cmd, args, "r")
  394 
  395   def read(self, bytes):
  396     return self.fp.read(bytes)
  397 
  398   def readline(self):
  399     return self.fp.readline()
  400 
  401   def close(self):
  402     try:
  403       if self.fp:
  404         self.fp.close()
  405         self.fp = None
  406     finally:
  407       try:
  408         if self.temp1:
  409           os.remove(self.temp1)
  410           self.temp1 = None
  411       finally:
  412         if self.temp2:
  413           os.remove(self.temp2)
  414           self.temp2 = None
  415 
  416   def __del__(self):
  417     self.close()
  418 
  419   def _label(self, (path, date, rev)):
  420     date = date and time.strftime('%Y/%m/%d %H:%M:%S', time.gmtime(date))
  421     return "%s\t%s\t%s" % (path, date, rev)
  422 
  423 
  424 def check_root_access(repos):
  425   """Return 1 iff the associated username is permitted to read REPOS,
  426   as determined by consulting REPOS's Authorizer object (if any)."""
  427 
  428   auth = repos.authorizer()
  429   if not auth:
  430     return 1
  431   return auth.check_root_access(repos.rootname())
  432   
  433 def check_path_access(repos, path_parts, pathtype=None, rev=None):
  434   """Return 1 iff the associated username is permitted to read
  435   revision REV of the path PATH_PARTS (of type PATHTYPE) in repository
  436   REPOS, as determined by consulting REPOS's Authorizer object (if any)."""
  437 
  438   auth = repos.authorizer()
  439   if not auth:
  440     return 1
  441   if not pathtype:
  442     pathtype = repos.itemtype(path_parts, rev)
  443   return auth.check_path_access(repos.rootname(), path_parts, pathtype, rev)
  444