File: symlinks.py

package info (click to toggle)
drslib 0.3.2-1
  • links: PTS, VCS
  • area: main
  • in suites: forky, sid, trixie
  • size: 5,544 kB
  • sloc: python: 6,119; xml: 988; makefile: 128; sh: 121
file content (117 lines) | stat: -rw-r--r-- 3,658 bytes parent folder | download | duplicates (4)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
#!/usr/bin/env python
# BSD Licence
# Copyright (c) 2011, Science & Technology Facilities Council (STFC)
# All rights reserved.
#
# See the LICENSE file in the source distribution of this software for
# the full license text.

import os
import re

"""
Finds symlinks recursively under a given top directory, and replaces any symlinks
found pointing to absolute paths with ones pointing to relative links.

Optionally a regular expression can be supplied to 
"""

class LinkFixer:

    def __init__(self, justTesting = False):
        self.justTesting = justTesting
        self._repeatSlashesSub = re.compile("//+").sub


    def noRepeatSlashes(self, path):
        """
        get path with any repeated slashes replaced by single /
        """
        return self._repeatSlashesSub("/", path)
  
  
    def getRelTarget(self, link, target):
        """
        get relative link target given existing link path and absolute target
        """
        link = self.noRepeatSlashes(link)
        target = self.noRepeatSlashes(target)

        commonPrefix = os.path.commonprefix([link, target])
        lenCommonPrefix = len(commonPrefix)
        linkWithoutCommonPrefix = link[lenCommonPrefix :]
        targetWithoutCommonPrefix = target[lenCommonPrefix :]

        # answer is then the remaining part of the target,
        # prepended by a "../" for every slash in the remaining part of the link
        answer = ""
        for char in linkWithoutCommonPrefix:
            if char == "/":
                answer += "../"
        answer += targetWithoutCommonPrefix
                
        return answer


    def fixLink(self, link, target):
        """
        Replace an absolute link with a relative link.
        """
        newTarget = self.getRelTarget(link, target)

        print "link:", link
        print "  current target:", target
        print "  new target:", newTarget

        if not self.justTesting:
            assert(os.path.islink(link))  # sanity check
            os.remove(link)
            os.symlink(newTarget, link)
            print "done"
        print


    def findAndFixLinks(self,
                        startingDirectory,
                        callback = None, 
                        regexp = None):
        """
        Call fixLink or other callback function on every absolute symlink found 
        under starting directory, matching regexp if supplied.
        Callback should take args: link path, link target
        """
        if not startingDirectory.startswith("/"):
            raise ValueError("starting directory should be an absolute path")

        if callback == None:
            callback = self.fixLink
        if regexp != None:
            tester = re.compile(regexp).search
        else:
            tester = None
        self._findAndFixLinks(startingDirectory, callback, tester)


    def _findAndFixLinks(self,
                         startingDirectory,
                         callback,
                         tester = None):

        for fileName in os.listdir(startingDirectory):
            path = os.path.join(startingDirectory, fileName)
            if os.path.islink(path):
                if (tester == None) or tester(fileName):
                    target = os.readlink(path)
                    if target.startswith("/"):
                        callback(path, target)
            else:
                if os.path.isdir(path):
                    self._findAndFixLinks(path, callback, tester)


if __name__ == '__main__':

    fixer = LinkFixer(justTesting = False)

    fixer.findAndFixLinks('/badc/cmip5/data/cmip5/output1/MOHC',
                          regexp = "\.nc$")