/usr/lib/python2.7/dist-packages/sphinx/util/inventory.py is in python-sphinx 1.6.7-1ubuntu1.
This file is owned by root:root, with mode 0o644.
The actual contents of the file can be viewed below.
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 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 | # -*- coding: utf-8 -*-
"""
sphinx.util.inventory
~~~~~~~~~~~~~~~~~~~~~
Inventory utility functions for Sphinx.
:copyright: Copyright 2007-2018 by the Sphinx team, see AUTHORS.
:license: BSD, see LICENSE for details.
"""
import re
import os
import zlib
from six import PY3
from sphinx.util import logging
if False:
# For type annotation
from typing import Callable, Dict, IO, Iterator, Tuple # NOQA
from sphinx.builders import Builder # NOQA
from sphinx.environment import BuildEnvironment # NOQA
if PY3:
unicode = str
Inventory = Dict[unicode, Dict[unicode, Tuple[unicode, unicode, unicode, unicode]]]
BUFSIZE = 16 * 1024
logger = logging.getLogger(__name__)
class InventoryFileReader(object):
"""A file reader for inventory file.
This reader supports mixture of texts and compressed texts.
"""
def __init__(self, stream):
# type: (IO) -> None
self.stream = stream
self.buffer = b''
self.eof = False
def read_buffer(self):
# type: () -> None
chunk = self.stream.read(BUFSIZE)
if chunk == b'':
self.eof = True
self.buffer += chunk
def readline(self):
# type: () -> unicode
pos = self.buffer.find(b'\n')
if pos != -1:
line = self.buffer[:pos].decode('utf-8')
self.buffer = self.buffer[pos + 1:]
elif self.eof:
line = self.buffer.decode('utf-8')
self.buffer = b''
else:
self.read_buffer()
line = self.readline()
return line
def readlines(self):
# type: () -> Iterator[unicode]
while not self.eof:
line = self.readline()
if line:
yield line
def read_compressed_chunks(self):
# type: () -> Iterator[bytes]
decompressor = zlib.decompressobj()
while not self.eof:
self.read_buffer()
yield decompressor.decompress(self.buffer)
self.buffer = b''
yield decompressor.flush()
def read_compressed_lines(self):
# type: () -> Iterator[unicode]
buf = b''
for chunk in self.read_compressed_chunks():
buf += chunk
pos = buf.find(b'\n')
while pos != -1:
yield buf[:pos].decode('utf-8')
buf = buf[pos + 1:]
pos = buf.find(b'\n')
class InventoryFile(object):
@classmethod
def load(cls, stream, uri, joinfunc):
# type: (IO, unicode, Callable) -> Inventory
reader = InventoryFileReader(stream)
line = reader.readline().rstrip()
if line == '# Sphinx inventory version 1':
return cls.load_v1(reader, uri, joinfunc)
elif line == '# Sphinx inventory version 2':
return cls.load_v2(reader, uri, joinfunc)
else:
raise ValueError('invalid inventory header: %s' % line)
@classmethod
def load_v1(cls, stream, uri, join):
# type: (InventoryFileReader, unicode, Callable) -> Inventory
invdata = {} # type: Inventory
projname = stream.readline().rstrip()[11:]
version = stream.readline().rstrip()[11:]
for line in stream.readlines():
name, type, location = line.rstrip().split(None, 2)
location = join(uri, location)
# version 1 did not add anchors to the location
if type == 'mod':
type = 'py:module'
location += '#module-' + name
else:
type = 'py:' + type
location += '#' + name
invdata.setdefault(type, {})[name] = (projname, version, location, '-')
return invdata
@classmethod
def load_v2(cls, stream, uri, join):
# type: (InventoryFileReader, unicode, Callable) -> Inventory
invdata = {} # type: Inventory
projname = stream.readline().rstrip()[11:]
version = stream.readline().rstrip()[11:]
line = stream.readline()
if 'zlib' not in line:
raise ValueError('invalid inventory header (not compressed): %s' % line)
for line in stream.read_compressed_lines():
# be careful to handle names with embedded spaces correctly
m = re.match(r'(?x)(.+?)\s+(\S*:\S*)\s+(-?\d+)\s+(\S+)\s+(.*)',
line.rstrip())
if not m:
continue
name, type, prio, location, dispname = m.groups()
if type == 'py:module' and type in invdata and \
name in invdata[type]: # due to a bug in 1.1 and below,
# two inventory entries are created
# for Python modules, and the first
# one is correct
continue
if location.endswith(u'$'):
location = location[:-1] + name
location = join(uri, location)
invdata.setdefault(type, {})[name] = (projname, version,
location, dispname)
return invdata
@classmethod
def dump(cls, filename, env, builder):
# type: (unicode, BuildEnvironment, Builder) -> None
def escape(string):
# type: (unicode) -> unicode
return re.sub("\\s+", " ", string)
with open(os.path.join(filename), 'wb') as f:
# header
f.write((u'# Sphinx inventory version 2\n'
u'# Project: %s\n'
u'# Version: %s\n'
u'# The remainder of this file is compressed using zlib.\n' %
(escape(env.config.project),
escape(env.config.version))).encode('utf-8'))
# body
compressor = zlib.compressobj(9)
for domainname, domain in sorted(env.domains.items()):
for name, dispname, typ, docname, anchor, prio in \
sorted(domain.get_objects()):
if anchor.endswith(name):
# this can shorten the inventory by as much as 25%
anchor = anchor[:-len(name)] + '$'
uri = builder.get_target_uri(docname)
if anchor:
uri += '#' + anchor
if dispname == name:
dispname = u'-'
entry = (u'%s %s:%s %s %s %s\n' %
(name, domainname, typ, prio, uri, dispname))
f.write(compressor.compress(entry.encode('utf-8')))
f.write(compressor.flush())
|