Source code for gavo.svcs.vanity
"""
Parsing and maintaining URL shortcuts.
"""
#c Copyright 2008-2023, the GAVO project <gavo@ari.uni-heidelberg.de>
#c
#c This program is free software, covered by the GNU GPL. See the
#c COPYING file in the source distribution.
import os
from gavo import base
from gavo import utils
[docs]class VanityLineError(base.Error):
"""parse error in vanity file.
"""
def __init__(self, msg, lineNo, src):
base.Error.__init__(msg)
self.msg, self.lineNo, self.src = msg, lineNo, src
def __str__(self):
return "Mapping file %s, line %d: %s"%(
repr(self.src), self.msg, self.lineNo)
BUILTIN_VANITY = """
__system__/products/p/get getproduct
__system__/products/p/dlasync datalinkuws
__system__/services/registry/pubreg.xml oai.xml
__system__/services/overview/external odoc
__system__/dc_tables/show/tablenote tablenote
__system__/dc_tables/show/tableinfo tableinfo
__system__/services/overview/admin seffe
__system__/services/overview/rdinfo browse
__system__/tap/run tap
__system__/adql/query/form adql !redirect
__system__/run/genrd genrd
"""
class _VanityMap(object):
"""a map of short resource paths to longer resource paths.
This is only used as singleton through getVanityMap.
There are two mappings: shortToLong, going from vanityName to
(fullPath,flags), and longToShort, mapping fullPath -> vanityName.
flags, in both cases, is a frozenset of flags. The only one defined
at this point is "!redirect".
"""
knownVanityOptions = set(["!redirect"])
def __init__(self):
self.shortToLong = {}
self.longToShort = {}
def _parseVanityLines(self, src):
lineNo = 0
for ln in src:
lineNo += 1
ln = ln.strip()
if not ln or ln.startswith("#"):
continue
parts = ln.split()
if not 1<len(parts)<4:
raise VanityLineError("Wrong number of words in '%s'"%ln, lineNo, src)
options = []
if len(parts)>2:
options.append(parts.pop())
if options[-1] not in self.knownVanityOptions:
raise VanityLineError("Bad option '%s'"%options[-1], lineNo, src)
dest, src = parts
yield src, dest, frozenset(options)
def _buildDicts(self, triples):
"""builds the child mappings from triples as yielded by _parseVanityLines.
"""
for short, int, flags in triples:
self.shortToLong[short] = (int, flags)
self.longToShort[int] = short
def addFromString(self, s):
"""adds vanity mappings from a string literal.
"""
self._buildDicts(
self._parseVanityLines(s.split("\n")))
def addFromFile(self, f):
"""adds vanity mappings from an open file.
"""
self._buildDicts(
self._parseVanityLines(f))
def _loadVanityMap():
"""helps getVanityMap.
"""
vm = _VanityMap()
vm.addFromString(BUILTIN_VANITY)
fSrc = os.path.join(base.getConfig("configDir"), "vanitynames.txt")
if os.path.exists(fSrc):
with open(fSrc) as f:
vm.addFromFile(f)
return vm
[docs]def getVanityMap():
"""returns "the" vanity map on this data center.
It consists of built-in vanity (without which things like product
delivery would actually break) and things read from etc/vanitynames.txt.
The input file format is documented in the DaCHS tutorial (The Vanity Map).
"""
try:
anchorRD = base.resolveCrossId("//services")
except (AttributeError, base.ReportableError):
# We're somewhere where we don't already have rscdesc loaded,
# or perhaps the database is down entirely.
# Let's assume that means the VanityMap will not need to be reloaded
# during the runtime, and it's ok if we anchor on the function.
anchorRD = getVanityMap
return utils.memoizeOn(
anchorRD,
_VanityMap,
_loadVanityMap)