comparison danboorufs.py @ 2:85cbd44f98b1 draft

Add license, (try to) respect the PEP 8, and don’t override the file builtin.
author Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
date Tue, 21 Aug 2012 20:17:49 +0200
parents 63ccd8b0d615
children 880904f1071f
comparison
equal deleted inserted replaced
1:63ccd8b0d615 2:85cbd44f98b1
1 #!/usr/bin/env python2 1 #!/usr/bin/env python2
2 # -*- encoding: utf-8 -*- 2 # -*- encoding: utf-8 -*-
3 #
4 #
5 # Copyright © 2012 Emmanuel Gil Peyrot <linkmauve@linkmauve.fr>
6 #
7 # Permission is hereby granted, free of charge, to any person obtaining a copy
8 # of this software and associated documentation files (the "Software"), to deal
9 # in the Software without restriction, including without limitation the rights
10 # to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
11 # copies of the Software, and to permit persons to whom the Software is
12 # furnished to do so, subject to the following conditions:
13 #
14 # The above copyright notice and this permission notice shall be included in
15 # all copies or substantial portions of the Software.
16 #
17 # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
18 # IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
19 # FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
20 # AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
21 # LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
22 # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE
23 # SOFTWARE.
24
3 25
4 from __future__ import with_statement 26 from __future__ import with_statement
5 27
6 from errno import ENOENT, ENOTDIR 28 from errno import ENOENT, ENOTDIR
7 from sys import argv, exit 29 from sys import argv
8 from threading import Lock 30 from threading import Lock
9 from time import time 31 from time import time
10 32
11 import os 33 import os
12 34
34 filename = name.replace('.tags', '') 56 filename = name.replace('.tags', '')
35 basename = os.path.basename(filename) 57 basename = os.path.basename(filename)
36 self.paths[basename] = filename 58 self.paths[basename] = filename
37 tags = [] 59 tags = []
38 self.files[basename] = tags 60 self.files[basename] = tags
39 with open(name, 'r') as file: 61 with open(name, 'r') as tagfile:
40 for line in file: 62 for line in tagfile:
41 for tag in line.split(): 63 for tag in line.split():
42 tag = tag.decode('UTF-8') 64 tag = tag.decode('UTF-8')
43 tag = tag.replace('/', u'�') #XXX 65 tag = tag.replace('/', u'�') #XXX
44 tags.append(tag) 66 tags.append(tag)
45 self.tags.setdefault(tag, []).append(basename) 67 self.tags.setdefault(tag, []).append(basename)
56 real_path = path[1:].split('/') 78 real_path = path[1:].split('/')
57 79
58 # Remove the leading - of tag exclusion. 80 # Remove the leading - of tag exclusion.
59 path = [tag[1:] if tag[0] == '-' else tag for tag in real_path] 81 path = [tag[1:] if tag[0] == '-' else tag for tag in real_path]
60 82
61 if filter(lambda tag: tag not in self.tags, path[:-1]): 83 for tag in path[:-1]:
62 raise FuseOSError(ENOENT) 84 if tag not in self.tags:
85 raise FuseOSError(ENOENT)
63 86
64 if path[-1] in self.tags: 87 if path[-1] in self.tags:
65 return (real_path, None) 88 return (real_path, None)
66 89
67 if path[-1] not in self.paths: 90 if path[-1] not in self.paths:
70 return (real_path[:-1], self.paths[real_path[-1]]) 93 return (real_path[:-1], self.paths[real_path[-1]])
71 94
72 def access(self, path, mode): 95 def access(self, path, mode):
73 self._split_path(path) 96 self._split_path(path)
74 97
75 def getattr(self, path, fh=None): 98 def getattr(self, path, file_handle=None):
76 tags, file = self._split_path(path) 99 _, filename = self._split_path(path)
77 path = file if file else self.root 100 path = filename if filename else self.root
78 st = os.lstat(path) 101 stat = os.lstat(path)
79 return dict((key, getattr(st, key)) for key in ('st_atime', 'st_ctime', 102 return dict((key, getattr(stat, key)) for key in ('st_atime',
80 'st_gid', 'st_mode', 'st_mtime', 'st_nlink', 'st_size', 'st_uid')) 103 'st_ctime', 'st_gid', 'st_mode', 'st_mtime',
104 'st_nlink', 'st_size', 'st_uid'))
81 105
82 getxattr = None 106 getxattr = None
83 listxattr = None 107 listxattr = None
108
84 def open(self, path, flags): 109 def open(self, path, flags):
85 tags, file = self._split_path(path) 110 _, filename = self._split_path(path)
86 return os.open(file, flags) 111 return os.open(filename, flags)
87 112
88 def read(self, path, size, offset, fh): 113 def read(self, path, size, offset, file_handle):
89 with self.rwlock: 114 with self.rwlock:
90 os.lseek(fh, offset, 0) 115 os.lseek(file_handle, offset, 0)
91 return os.read(fh, size) 116 return os.read(file_handle, size)
92 117
93 def readdir(self, path, fh): 118 def readdir(self, path, file_handle):
94 if path == '/': 119 if path == '/':
95 return ['.', '..'] + self.tags.keys() + self.files.keys() 120 return ['.', '..'] + self.tags.keys() + self.files.keys()
96 121
97 tags, file = self._split_path(path) 122 tags, filename = self._split_path(path)
98 if file: 123 if filename:
99 return FuseOSError(ENOTDIR) 124 return FuseOSError(ENOTDIR)
100 125
101 tags = set(tags) 126 tags = set(tags)
102 127
103 l = ' '.join(tags) 128 key = ' '.join(tags)
104 if l in self.cache: 129 if key in self.cache:
105 return ['.', '..'] + self.cache[l] 130 return ['.', '..'] + self.cache[key]
106 131
107 inclusion_tags = set(tag for tag in tags if tag[0] != '-') 132 inclusion_tags = set(tag for tag in tags if tag[0] != '-')
108 exclusion_tags = set(tag[1:] for tag in tags if tag[0] == '-') 133 exclusion_tags = set(tag[1:] for tag in tags if tag[0] == '-')
109 134
110 # Get the list of the files corresponding to those tags. 135 # Get the list of the files corresponding to those tags.
111 files = reduce((lambda s, t: s.intersection(self.tags[t])), inclusion_tags, set(self.files)) 136 files = reduce((lambda s, t: s.intersection(self.tags[t])),
112 files -= set([f for f in files if exclusion_tags.intersection(self.files[f])]) 137 inclusion_tags, set(self.files))
138 files -= set([f for f in files
139 if exclusion_tags.intersection(self.files[f])])
113 140
114 # Those next two steps are for useless tags removal. 141 # Those next two steps are for useless tags removal.
115 142
116 # Get the tags of those files. 143 # Get the tags of those files.
117 taglist = reduce((lambda s, f: s.union(self.files[f])), files, set()) 144 taglist = reduce((lambda s, f: s.union(self.files[f])), files, set())
118 taglist -= tags 145 taglist -= tags
119 146
120 # Remove the tags that can’t precise the file list anymore. 147 # Remove the tags that can’t precise the file list anymore.
121 rm = reduce((lambda s, f: s.intersection(self.files[f])), files, taglist) 148 remove = reduce((lambda s, f: s.intersection(self.files[f])), files,
122 taglist -= rm 149 taglist)
150 taglist -= remove
123 151
124 self.cache[l] = list(taglist) + list(files) 152 self.cache[key] = list(taglist) + list(files)
125 return ['.', '..'] + self.cache[l] 153 return ['.', '..'] + self.cache[key]
126 154
127 readlink = os.readlink 155 readlink = os.readlink
128 156
129 def release(self, path, fh): 157 def release(self, path, file_handle):
130 return os.close(fh) 158 return os.close(file_handle)
131 159
132 def statfs(self, path): 160 def statfs(self, path):
133 tags, file = self._split_path(path) 161 _, filename = self._split_path(path)
134 path = file if file else self.root 162 path = filename if filename else self.root
135 stv = os.statvfs(path) 163 stv = os.statvfs(path)
136 return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree', 164 return dict((key, getattr(stv, key)) for key in ('f_bavail', 'f_bfree',
137 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag', 165 'f_blocks', 'f_bsize', 'f_favail', 'f_ffree', 'f_files', 'f_flag',
138 'f_frsize', 'f_namemax')) 166 'f_frsize', 'f_namemax'))
139 167
145 print('usage: %s <tag file> [<tag file>...] <mountpoint>' % argv[0]) 173 print('usage: %s <tag file> [<tag file>...] <mountpoint>' % argv[0])
146 exit(1) 174 exit(1)
147 175
148 mountpoint = argv.pop() 176 mountpoint = argv.pop()
149 177
150 fuse = FUSE(Danbooru(argv[1:], os.path.dirname(mountpoint)), mountpoint, foreground=True) 178 fuse = FUSE(Danbooru(argv[1:], os.path.dirname(mountpoint)), mountpoint,
179 foreground=True)