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
33 has_pykpathsea
= False
36 # Locators implement an open method which returns a list of functions
37 # by searching for a file according to a specific rule. Each of the functions
38 # returned can be called (multiple times) and return an open file. The
39 # opening of the file might fail with a IOError which indicates, that the
40 # file could not be found at the given location.
41 # names is a list of kpsewhich format names to be used for searching where as
42 # extensions is a list of file extensions to be tried (including the dot). Note
43 # that the list of file extenions should include an empty string to not add
44 # an extension at all.
49 """locates files in the current directory"""
51 def openers(self
, filename
, names
, extensions
, mode
):
52 return [lambda: builtinopen(filename
+extension
, mode
) for extension
in extensions
]
54 locator_classes
["local"] = local
57 class internal_pkgutil
:
58 """locates files within the pyx data tree (via pkgutil)"""
60 def openers(self
, filename
, names
, extensions
, mode
):
61 for extension
in extensions
:
62 full_filename
= filename
+extension
63 dir = os
.path
.splitext(full_filename
)[1][1:]
65 data
= pkgutil
.get_data("pyx", "data/%s/%s" % (dir, full_filename
))
71 return [lambda: cStringIO
.StringIO(data
)]
74 """locates files within the pyx data tree (via an open relative to the path of this file)"""
76 def openers(self
, filename
, names
, extensions
, mode
):
78 for extension
in extensions
:
79 full_filename
= filename
+extension
80 dir = os
.path
.splitext(full_filename
)[1][1:]
81 result
.append(lambda: builtinopen(os
.path
.join(os
.path
.dirname(__file__
), "data", dir, full_filename
), mode
))
86 except AttributeError:
87 locator_classes
["internal"] = internal_open
# fallback for python < 2.6
89 locator_classes
["internal"] = internal_pkgutil
93 """locates files by searching recursively in a list of directories"""
96 self
.dirs
= config
.getlist("locator", "recursivedir")
97 self
.full_filenames
= {}
99 def openers(self
, filename
, names
, extensions
, mode
):
100 for extension
in extensions
:
101 if filename
+extension
in self
.full_filenames
:
102 return [lambda: builtinopen(self
.full_filenames
[filename
], mode
)]
104 dir = self
.dirs
.pop(0)
105 for item
in os
.listdir(dir):
106 full_item
= os
.path
.join(dir, item
)
107 if os
.path
.isdir(full_item
):
108 self
.dirs
.insert(0, full_item
)
110 self
.full_filenames
[item
] = full_item
111 for extension
in extensions
:
112 if filename
+extension
in self
.full_filenames
:
113 return [lambda: builtinopen(self
.full_filenames
[filename
], mode
)]
115 locator_classes
["recursivedir"] = recursivedir
119 """locates files by searching a list of ls-R files"""
122 self
.ls_Rs
= config
.getlist("locator", "ls-R")
123 self
.full_filenames
= {}
125 def openers(self
, filename
, names
, extensions
, mode
):
126 while self
.ls_Rs
and not any([filename
+extension
in self
.full_filenames
for extension
in extensions
]):
127 lsr
= self
.ls_Rs
.pop(0)
128 base_dir
= os
.path
.dirname(lsr
)
131 for line
in builtinopen(lsr
):
133 if first
and line
.startswith("%"):
136 if line
.endswith(":"):
137 dir = os
.path
.join(base_dir
, line
[:-1])
139 self
.full_filenames
[line
] = os
.path
.join(dir, line
)
140 for extension
in extensions
:
141 if filename
+extension
in self
.full_filenames
:
144 return builtinopen(self
.full_filenames
[filename
+extension
], mode
)
146 warnings
.warn("'%s' should be available at '%s' according to the ls-R file, "
147 "but the file is not available at this location; "
148 "update your ls-R file" % (filename
, self
.full_filenames
[filename
]))
151 locator_classes
["ls-R"] = ls_R
155 """locate files by pykpathsea (a C extension module wrapping libkpathsea)"""
157 def openers(self
, filename
, names
, extensions
, mode
):
158 if not has_pykpathsea
:
161 full_filename
= pykpathsea
.find_file(filename
, name
)
168 return builtinopen(full_filename
, mode
)
170 warnings
.warn("'%s' should be available at '%s' according to libkpathsea, "
171 "but the file is not available at this location; "
172 "update your kpsewhich database" % (filename
, full_filename
))
175 locator_classes
["pykpathsea"] = pykpathsea
179 # """locate files by libkpathsea using ctypes"""
181 # def openers(self, filename, names, extensions, mode):
182 # raise NotImplemented
184 # locator_classes["libpathsea"] = libkpathsea
188 """locate files using the kpsewhich executable"""
190 def openers(self
, filename
, names
, extensions
, mode
):
193 full_filenames
= pycompat
.popen('kpsewhich --format="%s" "%s"' % (name
, filename
)).read()
200 full_filename
= full_filenames
.split("\n")[0]
203 return builtinopen(full_filename
, mode
)
205 warnings
.warn("'%s' should be available at '%s' according to kpsewhich, "
206 "but the file is not available at this location; "
207 "update your kpsewhich database" % (filename
, full_filename
))
210 locator_classes
["kpsewhich"] = kpsewhich
214 """locate files using a locate executable"""
216 def openers(self
, filename
, names
, extensions
, mode
):
217 for extension
in extensions
:
218 full_filenames
= pycompat
.popen("locate \"%s\"" % (filename
+extension
)).read()
223 full_filename
= full_filenames
.split("\n")[0]
226 return builtinopen(full_filenames
, mode
)
228 warnings
.warn("'%s' should be available at '%s' according to the locate, "
229 "but the file is not available at this location; "
230 "update your locate database" % (filename
, self
.full_filenames
[filename
]))
233 locator_classes
["locate"] = locate
237 global methods
, opener_cache
239 print config
.getlist("locator", "methods", "local internal pykpathsea kpsewhich")
240 methods
= [locator_classes
[method
]()
241 for method
in config
.getlist("locator", "methods", "local internal pykpathsea kpsewhich")]
245 def open(filename
, formats
, mode
="r"):
246 """returns an open file searched according the list of formats"""
248 # When using an empty list of formats, the names list is empty
249 # and the extensions list contains an empty string only. For that
250 # case some locators (notably local and internal) return an open
251 # function for the requested file whereas other locators might not
252 # return anything (like pykpathsea and kpsewhich).
253 # This is useful for files not to be searched in the latex
254 # installation at all (like lfs files).
255 extensions
= pycompat
.set([""])
256 for format
in formats
:
257 for extension
in format
.extensions
:
258 extensions
.add(extension
)
259 names
= tuple([format
.name
for format
in formats
])
260 if (filename
, names
) in opener_cache
:
261 return opener_cache
[(filename
, names
)]()
262 for method
in methods
:
263 openers
= method
.openers(filename
, names
, extensions
, mode
)
265 for opener
in openers
:
271 opener_cache
[(filename
, names
)] = opener
273 raise IOError("Could not locate the file '%s'." % filename
)
277 def __init__(self
, name
, extensions
):
279 self
.extensions
= extensions
281 format
.tfm
= format("tfm", [".tfm"])
282 format
.afm
= format("afm", [".afm"])
283 format
.fontmap
= format("map", [])
284 format
.pict
= format("graphics/figure", [".eps", ".epsi"])
285 format
.tex_ps_header
= format("PostScript header", [".pro"]) # contains also: enc files
286 format
.type1
= format("type1 fonts", [".pfa", ".pfb"])
287 format
.vf
= format("vf", [".vf"])
288 format
.dvips_config
= format("dvips config", [])