1 # -*- encoding: utf-8 -*-
4 # Copyright (C) 2011 Jörg Lehmann <joergl@users.sourceforge.net>
5 # Copyright (C) 2009-2011 André Wobst <wobsta@users.sourceforge.net>
7 # This file is part of PyX (http://pyx.sourceforge.net/).
9 # PyX is free software; you can redistribute it and/or modify
10 # it under the terms of the GNU General Public License as published by
11 # the Free Software Foundation; either version 2 of the License, or
12 # (at your option) any later version.
14 # PyX is distributed in the hope that it will be useful,
15 # but WITHOUT ANY WARRANTY; without even the implied warranty of
16 # MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 # GNU General Public License for more details.
19 # You should have received a copy of the GNU General Public License
20 # along with PyX; if not, write to the Free Software
21 # Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA
25 import os
, cStringIO
, warnings
, pkgutil
27 import config
, pycompat
30 # Locators implement an open method which returns a list of functions
31 # by searching for a file according to a specific rule. Each of the functions
32 # returned can be called (multiple times) and return an open file. The
33 # opening of the file might fail with a IOError which indicates, that the
34 # file could not be found at the given location.
35 # names is a list of kpsewhich format names to be used for searching where as
36 # extensions is a list of file extensions to be tried (including the dot). Note
37 # that the list of file extenions should include an empty string to not add
38 # an extension at all.
43 """locates files in the current directory"""
45 def openers(self
, filename
, names
, extensions
, mode
):
46 return [lambda: builtinopen(filename
+extension
, mode
) for extension
in extensions
]
48 locator_classes
["local"] = local
51 class internal_pkgutil
:
52 """locates files within the pyx data tree (via pkgutil)"""
54 def openers(self
, filename
, names
, extensions
, mode
):
55 for extension
in extensions
:
56 full_filename
= filename
+extension
57 dir = os
.path
.splitext(full_filename
)[1][1:]
59 data
= pkgutil
.get_data("pyx", "data/%s/%s" % (dir, full_filename
))
65 return [lambda: cStringIO
.StringIO(data
)]
68 """locates files within the pyx data tree (via an open relative to the path of this file)"""
70 def openers(self
, filename
, names
, extensions
, mode
):
72 for extension
in extensions
:
73 full_filename
= filename
+extension
74 dir = os
.path
.splitext(full_filename
)[1][1:]
75 result
.append(lambda: builtinopen(os
.path
.join(os
.path
.dirname(__file__
), "data", dir, full_filename
), mode
))
80 except AttributeError:
81 locator_classes
["internal"] = internal_open
# fallback for python < 2.6
83 locator_classes
["internal"] = internal_pkgutil
87 """locates files by searching recursively in a list of directories"""
90 self
.dirs
= config
.getlist("locator", "recursivedir")
91 self
.full_filenames
= {}
93 def openers(self
, filename
, names
, extensions
, mode
):
94 for extension
in extensions
:
95 if filename
+extension
in self
.full_filenames
:
96 return [lambda: builtinopen(self
.full_filenames
[filename
], mode
)]
98 dir = self
.dirs
.pop(0)
99 for item
in os
.listdir(dir):
100 full_item
= os
.path
.join(dir, item
)
101 if os
.path
.isdir(full_item
):
102 self
.dirs
.insert(0, full_item
)
104 self
.full_filenames
[item
] = full_item
105 for extension
in extensions
:
106 if filename
+extension
in self
.full_filenames
:
107 return [lambda: builtinopen(self
.full_filenames
[filename
], mode
)]
109 locator_classes
["recursivedir"] = recursivedir
113 """locates files by searching a list of ls-R files"""
116 self
.ls_Rs
= config
.getlist("locator", "ls-R")
117 self
.full_filenames
= {}
119 def openers(self
, filename
, names
, extensions
, mode
):
120 while self
.ls_Rs
and not any([filename
+extension
in self
.full_filenames
for extension
in extensions
]):
121 lsr
= self
.ls_Rs
.pop(0)
122 base_dir
= os
.path
.dirname(lsr
)
125 for line
in builtinopen(lsr
):
127 if first
and line
.startswith("%"):
130 if line
.endswith(":"):
131 dir = os
.path
.join(base_dir
, line
[:-1])
133 self
.full_filenames
[line
] = os
.path
.join(dir, line
)
134 for extension
in extensions
:
135 if filename
+extension
in self
.full_filenames
:
138 return builtinopen(self
.full_filenames
[filename
+extension
], mode
)
140 warnings
.warn("'%s' should be available at '%s' according to the ls-R file, "
141 "but the file is not available at this location; "
142 "update your ls-R file" % (filename
, self
.full_filenames
[filename
]))
145 locator_classes
["ls-R"] = ls_R
149 """locate files by pykpathsea (a C extension module wrapping libkpathsea)"""
151 def openers(self
, filename
, names
, extensions
, mode
):
154 full_filename
= pykpathsea
.find_file(filename
, name
)
161 return builtinopen(full_filename
, mode
)
163 warnings
.warn("'%s' should be available at '%s' according to libkpathsea, "
164 "but the file is not available at this location; "
165 "update your kpsewhich database" % (filename
, full_filename
))
168 locator_classes
["pykpathsea"] = pykpathsea
172 # """locate files by libkpathsea using ctypes"""
174 # def openers(self, filename, names, extensions, mode):
175 # raise NotImplemented
177 # locator_classes["libpathsea"] = libkpathsea
181 """locate files using the kpsewhich executable"""
183 def openers(self
, filename
, names
, extensions
, mode
):
186 full_filenames
= pycompat
.popen('kpsewhich --format="%s" "%s"' % (name
, filename
)).read()
193 full_filename
= full_filenames
.split("\n")[0]
196 return builtinopen(full_filename
, mode
)
198 warnings
.warn("'%s' should be available at '%s' according to kpsewhich, "
199 "but the file is not available at this location; "
200 "update your kpsewhich database" % (filename
, full_filename
))
203 locator_classes
["kpsewhich"] = kpsewhich
207 """locate files using a locate executable"""
209 def openers(self
, filename
, names
, extensions
, mode
):
210 for extension
in extensions
:
211 full_filenames
= pycompat
.popen("locate \"%s\"" % (filename
+extension
)).read()
216 full_filename
= full_filenames
.split("\n")[0]
219 return builtinopen(full_filenames
, mode
)
221 warnings
.warn("'%s' should be available at '%s' according to the locate, "
222 "but the file is not available at this location; "
223 "update your locate database" % (filename
, self
.full_filenames
[filename
]))
226 locator_classes
["locate"] = locate
229 methods
= [locator_classes
[method
]()
230 for method
in config
.getlist("locator", "methods", "local internal pykpathsea kpsewhich")]
234 def open(filename
, formats
, mode
="r"):
235 """returns an open file searched according the list of formats
237 When using an empty list of formats, the names list is empty
238 and the extensions list contains an empty string only. For that
239 case some locators (notably local and internal) return an open
240 function for the requested file whereas other locators might not
241 return anything (like pykpathsea and kpsewhich) as the names list
242 is empty. This is useful for files not to be searched in the latex
243 installation at all (like lfs files)."""
244 extensions
= pycompat
.set([""])
245 for format
in formats
:
246 for extension
in format
.extensions
:
247 extensions
.add(extension
)
248 names
= tuple([format
.name
for format
in formats
])
249 if (filename
, names
) in opener_cache
:
250 return opener_cache
[(filename
, names
)]()
251 for method
in methods
:
252 openers
= method
.openers(filename
, names
, extensions
, mode
)
254 for opener
in openers
:
260 opener_cache
[(filename
, names
)] = opener
262 raise IOError("Could not locate the file '%s'." % filename
)
266 def __init__(self
, name
, extensions
):
268 self
.extensions
= extensions
270 format
.tfm
= format("tfm", [".tfm"])
271 format
.afm
= format("afm", [".afm"])
272 format
.fontmap
= format("map", [])
273 format
.pict
= format("graphics/figure", [".eps", ".epsi"])
274 format
.tex_ps_header
= format("PostScript header", [".pro"]) # contains also: enc files
275 format
.type1
= format("type1 fonts", [".pfa", ".pfb"])
276 format
.vf
= format("vf", [".vf"])
277 format
.dvips_config
= format("dvips config", [])