#
+#
# History:
+# 2017-22-07 mb Add RLE decompression
+# 2016-16-10 mb Add save method without compression
# 1995-09-10 fl Created
#
+# Copyright (c) 2016 by Mickael Bonfill.
# Copyright (c) 2008 by Karsten Hiddemann.
# Copyright (c) 1997 by Secret Labs AB.
# Copyright (c) 1995 by Fredrik Lundh.
@@ -18,21 +22,34 @@
#
-from PIL import Image, ImageFile, _binary
+from . import Image, ImageFile
+from ._binary import i8, o8, i16be as i16
+from ._util import py3
+import struct
+import os
-__version__ = "0.2"
-i8 = _binary.i8
-i16 = _binary.i16be
+__version__ = "0.3"
def _accept(prefix):
return len(prefix) >= 2 and i16(prefix) == 474
+MODES = {
+ (1, 1, 1): "L",
+ (1, 2, 1): "L",
+ (2, 1, 1): "L;16B",
+ (2, 2, 1): "L;16B",
+ (1, 3, 3): "RGB",
+ (2, 3, 3): "RGB;16B",
+ (1, 3, 4): "RGBA",
+ (2, 3, 4): "RGBA;16B"
+}
+
+
##
# Image plugin for SGI images.
-
class SgiImageFile(ImageFile.ImageFile):
format = "SGI"
@@ -41,49 +58,170 @@ class SgiImageFile(ImageFile.ImageFile):
def _open(self):
# HEAD
- s = self.fp.read(512)
+ headlen = 512
+ s = self.fp.read(headlen)
+
+ # magic number : 474
if i16(s) != 474:
raise ValueError("Not an SGI image file")
- # relevant header entries
+ # compression : verbatim or RLE
compression = i8(s[2])
- # bytes, dimension, zsize
- layout = i8(s[3]), i16(s[4:]), i16(s[10:])
+ # bpc : 1 or 2 bytes (8bits or 16bits)
+ bpc = i8(s[3])
- # determine mode from bytes/zsize
- if layout == (1, 2, 1) or layout == (1, 1, 1):
- self.mode = "L"
- elif layout == (1, 3, 3):
- self.mode = "RGB"
- elif layout == (1, 3, 4):
- self.mode = "RGBA"
- else:
+ # dimension : 1, 2 or 3 (depending on xsize, ysize and zsize)
+ dimension = i16(s[4:])
+
+ # xsize : width
+ xsize = i16(s[6:])
+
+ # ysize : height
+ ysize = i16(s[8:])
+
+ # zsize : channels count
+ zsize = i16(s[10:])
+
+ # layout
+ layout = bpc, dimension, zsize
+
+ # determine mode from bits/zsize
+ rawmode = ""
+ try:
+ rawmode = MODES[layout]
+ except KeyError:
+ pass
+
+ if rawmode == "":
raise ValueError("Unsupported SGI image mode")
- # size
- self.size = i16(s[6:]), i16(s[8:])
+ self.size = xsize, ysize
+ self.mode = rawmode.split(";")[0]
+
+ # orientation -1 : scanlines begins at the bottom-left corner
+ orientation = -1
# decoder info
if compression == 0:
- offset = 512
- pagesize = self.size[0]*self.size[1]*layout[0]
- self.tile = []
- for layer in self.mode:
- self.tile.append(
- ("raw", (0, 0)+self.size, offset, (layer, 0, -1)))
- offset = offset + pagesize
+ pagesize = xsize * ysize * bpc
+ if bpc == 2:
+ self.tile = [("SGI16", (0, 0) + self.size,
+ headlen, (self.mode, 0, orientation))]
+ else:
+ self.tile = []
+ offset = headlen
+ for layer in self.mode:
+ self.tile.append(
+ ("raw", (0, 0) + self.size,
+ offset, (layer, 0, orientation)))
+ offset += pagesize
elif compression == 1:
- raise ValueError("SGI RLE encoding not supported")
+ self.tile = [("sgi_rle", (0, 0) + self.size,
+ headlen, (rawmode, orientation, bpc))]
+
+
+def _save(im, fp, filename):
+ if im.mode != "RGB" and im.mode != "RGBA" and im.mode != "L":
+ raise ValueError("Unsupported SGI image mode")
+
+ # Get the keyword arguments
+ info = im.encoderinfo
+
+ # Byte-per-pixel precision, 1 = 8bits per pixel
+ bpc = info.get("bpc", 1)
+
+ if bpc not in (1, 2):
+ raise ValueError("Unsupported number of bytes per pixel")
+
+ # Flip the image, since the origin of SGI file is the bottom-left corner
+ orientation = -1
+ # Define the file as SGI File Format
+ magicNumber = 474
+ # Run-Length Encoding Compression - Unsupported at this time
+ rle = 0
+
+ # Number of dimensions (x,y,z)
+ dim = 3
+ # X Dimension = width / Y Dimension = height
+ x, y = im.size
+ if im.mode == "L" and y == 1:
+ dim = 1
+ elif im.mode == "L":
+ dim = 2
+ # Z Dimension: Number of channels
+ z = len(im.mode)
+
+ if dim == 1 or dim == 2:
+ z = 1
+
+ # assert we've got the right number of bands.
+ if len(im.getbands()) != z:
+ raise ValueError("incorrect number of bands in SGI write: %s vs %s" %
+ (z, len(im.getbands())))
+
+ # Minimum Byte value
+ pinmin = 0
+ # Maximum Byte value (255 = 8bits per pixel)
+ pinmax = 255
+ # Image name (79 characters max, truncated below in write)
+ imgName = os.path.splitext(os.path.basename(filename))[0]
+ if py3:
+ imgName = imgName.encode('ascii', 'ignore')
+ # Standard representation of pixel in the file
+ colormap = 0
+ fp.write(struct.pack('>h', magicNumber))
+ fp.write(o8(rle))
+ fp.write(o8(bpc))
+ fp.write(struct.pack('>H', dim))
+ fp.write(struct.pack('>H', x))
+ fp.write(struct.pack('>H', y))
+ fp.write(struct.pack('>H', z))
+ fp.write(struct.pack('>l', pinmin))
+ fp.write(struct.pack('>l', pinmax))
+ fp.write(struct.pack('4s', b'')) # dummy
+ fp.write(struct.pack('79s', imgName)) # truncates to 79 chars
+ fp.write(struct.pack('s', b'')) # force null byte after imgname
+ fp.write(struct.pack('>l', colormap))
+ fp.write(struct.pack('404s', b'')) # dummy
+
+ rawmode = 'L'
+ if bpc == 2:
+ rawmode = 'L;16B'
+
+ for channel in im.split():
+ fp.write(channel.tobytes('raw', rawmode, 0, orientation))
+
+ fp.close()
+
+
+class SGI16Decoder(ImageFile.PyDecoder):
+ _pulls_fd = True
+
+ def decode(self, buffer):
+ rawmode, stride, orientation = self.args
+ pagesize = self.state.xsize * self.state.ysize
+ zsize = len(self.mode)
+ self.fd.seek(512)
+
+ for band in range(zsize):
+ channel = Image.new('L', (self.state.xsize, self.state.ysize))
+ channel.frombytes(self.fd.read(2 * pagesize), 'raw',
+ 'L;16B', stride, orientation)
+ self.im.putband(channel.im, band)
+
+ return -1, 0
#
# registry
-Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
-Image.register_extension(SgiImageFile.format, ".bw")
-Image.register_extension(SgiImageFile.format, ".rgb")
-Image.register_extension(SgiImageFile.format, ".rgba")
-Image.register_extension(SgiImageFile.format, ".sgi")
+Image.register_decoder("SGI16", SGI16Decoder)
+Image.register_open(SgiImageFile.format, SgiImageFile, _accept)
+Image.register_save(SgiImageFile.format, _save)
+Image.register_mime(SgiImageFile.format, "image/sgi")
+Image.register_mime(SgiImageFile.format, "image/rgb")
+
+Image.register_extensions(SgiImageFile.format, [".bw", ".rgb", ".rgba", ".sgi"])
# End of file
diff --git a/server/www/packages/packages-windows/x86/PIL/SpiderImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/SpiderImagePlugin.py
index 07f623c..d502779 100644
--- a/server/www/packages/packages-windows/x86/PIL/SpiderImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/SpiderImagePlugin.py
@@ -27,10 +27,10 @@
# image data from electron microscopy and tomography.
#
# Spider home page:
-# http://spider.wadsworth.org/spider_doc/spider/docs/spider.html
+# https://spider.wadsworth.org/spider_doc/spider/docs/spider.html
#
# Details about the Spider image format:
-# http://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html
+# https://spider.wadsworth.org/spider_doc/spider/docs/image_doc.html
#
from __future__ import print_function
@@ -48,17 +48,16 @@ def isInt(f):
return 1
else:
return 0
- except ValueError:
- return 0
- except OverflowError:
+ except (ValueError, OverflowError):
return 0
+
iforms = [1, 3, -11, -12, -21, -22]
# There is no magic number to identify Spider files, so just check a
# series of header locations to see if they have reasonable values.
-# Returns no.of bytes in the header, if it is a valid Spider header,
+# Returns no. of bytes in the header, if it is a valid Spider header,
# otherwise returns 0
def isSpiderHeader(t):
@@ -75,7 +74,7 @@ def isSpiderHeader(t):
labrec = int(h[13]) # no. records in file header
labbyt = int(h[22]) # total no. of bytes in header
lenbyt = int(h[23]) # record length in bytes
- # print "labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt)
+ # print("labrec = %d, labbyt = %d, lenbyt = %d" % (labrec,labbyt,lenbyt))
if labbyt != (labrec * lenbyt):
return 0
# looks like a valid header
@@ -83,9 +82,8 @@ def isSpiderHeader(t):
def isSpiderImage(filename):
- fp = open(filename, 'rb')
- f = fp.read(92) # read 23 * 4 bytes
- fp.close()
+ with open(filename, 'rb') as fp:
+ f = fp.read(92) # read 23 * 4 bytes
t = struct.unpack('>23f', f) # try big-endian first
hdrlen = isSpiderHeader(t)
if hdrlen == 0:
@@ -98,6 +96,7 @@ class SpiderImageFile(ImageFile.ImageFile):
format = "SPIDER"
format_description = "Spider 2D image"
+ _close_exclusive_fp_after_loading = False
def _open(self):
# check header
@@ -174,8 +173,8 @@ class SpiderImageFile(ImageFile.ImageFile):
def seek(self, frame):
if self.istack == 0:
raise EOFError("attempt to seek in a non-stack file")
- if frame >= self._nimages:
- raise EOFError("attempt to seek past end of file")
+ if not self._seek_check(frame):
+ return
self.stkoffset = self.hdrlen + frame * (self.hdrlen + self.imgbytes)
self.fp = self.__fp
self.fp.seek(self.stkoffset)
@@ -201,7 +200,7 @@ class SpiderImageFile(ImageFile.ImageFile):
# given a list of filenames, return a list of images
def loadImageSeries(filelist=None):
- " create a list of Image.images for use in montage "
+ """create a list of Image.images for use in montage"""
if filelist is None or len(filelist) < 1:
return
@@ -267,17 +266,11 @@ def _save(im, fp, filename):
raise IOError("Error creating Spider header")
# write the SPIDER header
- try:
- fp = open(filename, 'wb')
- except:
- raise IOError("Unable to open %s for writing" % filename)
fp.writelines(hdr)
rawmode = "F;32NF" # 32-bit native floating point
ImageFile._save(im, fp, [("raw", (0, 0)+im.size, 0, (rawmode, 0, 1))])
- fp.close()
-
def _save_spider(im, fp, filename):
# get the filename extension and register it with Image
@@ -287,12 +280,13 @@ def _save_spider(im, fp, filename):
# --------------------------------------------------------------------
+
Image.register_open(SpiderImageFile.format, SpiderImageFile)
Image.register_save(SpiderImageFile.format, _save_spider)
if __name__ == "__main__":
- if not sys.argv[1:]:
+ if len(sys.argv) < 2:
print("Syntax: python SpiderImagePlugin.py [infile] [outfile]")
sys.exit()
@@ -301,10 +295,6 @@ if __name__ == "__main__":
print("input image must be in Spider format")
sys.exit()
- outfile = ""
- if len(sys.argv[1:]) > 1:
- outfile = sys.argv[2]
-
im = Image.open(filename)
print("image: " + str(im))
print("format: " + str(im.format))
@@ -313,7 +303,9 @@ if __name__ == "__main__":
print("max, min: ", end=' ')
print(im.getextrema())
- if outfile != "":
+ if len(sys.argv) > 2:
+ outfile = sys.argv[2]
+
# perform some image operation
im = im.transpose(Image.FLIP_LEFT_RIGHT)
print(
diff --git a/server/www/packages/packages-windows/x86/PIL/SunImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/SunImagePlugin.py
index af63144..fd5e827 100644
--- a/server/www/packages/packages-windows/x86/PIL/SunImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/SunImagePlugin.py
@@ -17,12 +17,11 @@
#
-from PIL import Image, ImageFile, ImagePalette, _binary
+from . import Image, ImageFile, ImagePalette
+from ._binary import i32be as i32
__version__ = "0.3"
-i32 = _binary.i32be
-
def _accept(prefix):
return len(prefix) >= 4 and i32(prefix) == 0x59a66a95
@@ -38,6 +37,21 @@ class SunImageFile(ImageFile.ImageFile):
def _open(self):
+ # The Sun Raster file header is 32 bytes in length
+ # and has the following format:
+
+ # typedef struct _SunRaster
+ # {
+ # DWORD MagicNumber; /* Magic (identification) number */
+ # DWORD Width; /* Width of image in pixels */
+ # DWORD Height; /* Height of image in pixels */
+ # DWORD Depth; /* Number of bits per pixel */
+ # DWORD Length; /* Size of image data in bytes */
+ # DWORD Type; /* Type of raster file */
+ # DWORD ColorMapType; /* Type of color map */
+ # DWORD ColorMapLength; /* Size of the color map in bytes */
+ # } SUNRASTER;
+
# HEAD
s = self.fp.read(32)
if i32(s) != 0x59a66a95:
@@ -48,34 +62,75 @@ class SunImageFile(ImageFile.ImageFile):
self.size = i32(s[4:8]), i32(s[8:12])
depth = i32(s[12:16])
+ data_length = i32(s[16:20]) # unreliable, ignore.
+ file_type = i32(s[20:24])
+ palette_type = i32(s[24:28]) # 0: None, 1: RGB, 2: Raw/arbitrary
+ palette_length = i32(s[28:32])
+
if depth == 1:
self.mode, rawmode = "1", "1;I"
+ elif depth == 4:
+ self.mode, rawmode = "L", "L;4"
elif depth == 8:
self.mode = rawmode = "L"
elif depth == 24:
- self.mode, rawmode = "RGB", "BGR"
+ if file_type == 3:
+ self.mode, rawmode = "RGB", "RGB"
+ else:
+ self.mode, rawmode = "RGB", "BGR"
+ elif depth == 32:
+ if file_type == 3:
+ self.mode, rawmode = 'RGB', 'RGBX'
+ else:
+ self.mode, rawmode = 'RGB', 'BGRX'
else:
- raise SyntaxError("unsupported mode")
+ raise SyntaxError("Unsupported Mode/Bit Depth")
- compression = i32(s[20:24])
+ if palette_length:
+ if palette_length > 1024:
+ raise SyntaxError("Unsupported Color Palette Length")
- if i32(s[24:28]) != 0:
- length = i32(s[28:32])
- offset = offset + length
- self.palette = ImagePalette.raw("RGB;L", self.fp.read(length))
+ if palette_type != 1:
+ raise SyntaxError("Unsupported Palette Type")
+
+ offset = offset + palette_length
+ self.palette = ImagePalette.raw("RGB;L", self.fp.read(palette_length))
if self.mode == "L":
- self.mode = rawmode = "P"
+ self.mode = "P"
+ rawmode = rawmode.replace('L', 'P')
- stride = (((self.size[0] * depth + 7) // 8) + 3) & (~3)
+ # 16 bit boundaries on stride
+ stride = ((self.size[0] * depth + 15) // 16) * 2
- if compression == 1:
+ # file type: Type is the version (or flavor) of the bitmap
+ # file. The following values are typically found in the Type
+ # field:
+ # 0000h Old
+ # 0001h Standard
+ # 0002h Byte-encoded
+ # 0003h RGB format
+ # 0004h TIFF format
+ # 0005h IFF format
+ # FFFFh Experimental
+
+ # Old and standard are the same, except for the length tag.
+ # byte-encoded is run-length-encoded
+ # RGB looks similar to standard, but RGB byte order
+ # TIFF and IFF mean that they were converted from T/IFF
+ # Experimental means that it's something else.
+ # (https://www.fileformat.info/format/sunraster/egff.htm)
+
+ if file_type in (0, 1, 3, 4, 5):
self.tile = [("raw", (0, 0)+self.size, offset, (rawmode, stride))]
- elif compression == 2:
+ elif file_type == 2:
self.tile = [("sun_rle", (0, 0)+self.size, offset, rawmode)]
+ else:
+ raise SyntaxError('Unsupported Sun Raster file type')
#
# registry
+
Image.register_open(SunImageFile.format, SunImageFile, _accept)
Image.register_extension(SunImageFile.format, ".ras")
diff --git a/server/www/packages/packages-windows/x86/PIL/TarIO.py b/server/www/packages/packages-windows/x86/PIL/TarIO.py
index 4e5115b..0e949ff 100644
--- a/server/www/packages/packages-windows/x86/PIL/TarIO.py
+++ b/server/www/packages/packages-windows/x86/PIL/TarIO.py
@@ -14,7 +14,7 @@
# See the README file for information on usage and redistribution.
#
-from PIL import ContainerIO
+from . import ContainerIO
##
@@ -23,14 +23,13 @@ from PIL import ContainerIO
class TarIO(ContainerIO.ContainerIO):
- ##
- # Create file object.
- #
- # @param tarfile Name of TAR file.
- # @param file Name of member file.
-
def __init__(self, tarfile, file):
+ """
+ Create file object.
+ :param tarfile: Name of TAR file.
+ :param file: Name of member file.
+ """
fh = open(tarfile, "rb")
while True:
diff --git a/server/www/packages/packages-windows/x86/PIL/TgaImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/TgaImagePlugin.py
index a75ce29..57b6ae2 100644
--- a/server/www/packages/packages-windows/x86/PIL/TgaImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/TgaImagePlugin.py
@@ -17,7 +17,8 @@
#
-from PIL import Image, ImageFile, ImagePalette, _binary
+from . import Image, ImageFile, ImagePalette
+from ._binary import i8, i16le as i16, o8, o16le as o16
__version__ = "0.3"
@@ -26,15 +27,13 @@ __version__ = "0.3"
# --------------------------------------------------------------------
# Read RGA file
-i8 = _binary.i8
-i16 = _binary.i16le
-
MODES = {
# map imagetype/depth to rawmode
(1, 8): "P",
(3, 1): "1",
(3, 8): "L",
+ (3, 16): "LA",
(2, 16): "BGR;5",
(2, 24): "BGR",
(2, 32): "BGRA",
@@ -76,6 +75,8 @@ class TgaImageFile(ImageFile.ImageFile):
self.mode = "L"
if depth == 1:
self.mode = "1" # ???
+ elif depth == 16:
+ self.mode = "LA"
elif imagetype in (1, 9):
self.mode = "P"
elif imagetype in (2, 10):
@@ -132,35 +133,35 @@ class TgaImageFile(ImageFile.ImageFile):
# --------------------------------------------------------------------
# Write TGA file
-o8 = _binary.o8
-o16 = _binary.o16le
-o32 = _binary.o32le
SAVE = {
"1": ("1", 1, 0, 3),
"L": ("L", 8, 0, 3),
+ "LA": ("LA", 16, 0, 3),
"P": ("P", 8, 1, 1),
"RGB": ("BGR", 24, 0, 2),
"RGBA": ("BGRA", 32, 0, 2),
}
-def _save(im, fp, filename, check=0):
+def _save(im, fp, filename):
try:
rawmode, bits, colormaptype, imagetype = SAVE[im.mode]
except KeyError:
raise IOError("cannot write mode %s as TGA" % im.mode)
- if check:
- return check
+ rle = im.encoderinfo.get("rle", False)
+
+ if rle:
+ imagetype += 8
if colormaptype:
colormapfirst, colormaplength, colormapentry = 0, 256, 24
else:
colormapfirst, colormaplength, colormapentry = 0, 0, 0
- if im.mode == "RGBA":
+ if im.mode in ("LA", "RGBA"):
flags = 8
else:
flags = 0
@@ -185,13 +186,23 @@ def _save(im, fp, filename, check=0):
if colormaptype:
fp.write(im.im.getpalette("RGB", "BGR"))
- ImageFile._save(
- im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
+ if rle:
+ ImageFile._save(
+ im,
+ fp,
+ [("tga_rle", (0, 0) + im.size, 0, (rawmode, orientation))])
+ else:
+ ImageFile._save(
+ im, fp, [("raw", (0, 0) + im.size, 0, (rawmode, 0, orientation))])
+
+ # write targa version 2 footer
+ fp.write(b"\000" * 8 + b"TRUEVISION-XFILE." + b"\000")
#
# --------------------------------------------------------------------
# Registry
+
Image.register_open(TgaImageFile.format, TgaImageFile)
Image.register_save(TgaImageFile.format, _save)
diff --git a/server/www/packages/packages-windows/x86/PIL/TiffImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/TiffImagePlugin.py
index 524d42a..6f032f4 100644
--- a/server/www/packages/packages-windows/x86/PIL/TiffImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/TiffImagePlugin.py
@@ -41,10 +41,9 @@
from __future__ import division, print_function
-from PIL import Image, ImageFile
-from PIL import ImagePalette
-from PIL import _binary
-from PIL import TiffTags
+from . import Image, ImageFile, ImagePalette, TiffTags
+from ._binary import i8, o8
+from ._util import py3
import collections
from fractions import Fraction
@@ -59,6 +58,13 @@ import warnings
from .TiffTags import TYPES
+try:
+ # Python 3
+ from collections.abc import MutableMapping
+except ImportError:
+ # Python 2.7
+ from collections import MutableMapping
+
__version__ = "1.3.5"
DEBUG = False # Needs to be merged with the new logging approach.
@@ -71,9 +77,6 @@ IFD_LEGACY_API = True
II = b"II" # little-endian (Intel style)
MM = b"MM" # big-endian (Motorola style)
-i8 = _binary.i8
-o8 = _binary.o8
-
#
# --------------------------------------------------------------------
# Read TIFF files
@@ -132,7 +135,7 @@ COMPRESSION_INFO = {
34677: "tiff_sgilog24",
}
-COMPRESSION_INFO_REV = dict([(v, k) for (k, v) in COMPRESSION_INFO.items()])
+COMPRESSION_INFO_REV = {v: k for k, v in COMPRESSION_INFO.items()}
OPEN_INFO = {
# (ByteOrder, PhotoInterpretation, SampleFormat, FillOrder, BitsPerSample,
@@ -177,14 +180,14 @@ OPEN_INFO = {
(II, 1, (1,), 1, (16,), ()): ("I;16", "I;16"),
(MM, 1, (1,), 1, (16,), ()): ("I;16B", "I;16B"),
- (II, 1, (2,), 1, (16,), ()): ("I;16S", "I;16S"),
- (MM, 1, (2,), 1, (16,), ()): ("I;16BS", "I;16BS"),
+ (II, 1, (2,), 1, (16,), ()): ("I", "I;16S"),
+ (MM, 1, (2,), 1, (16,), ()): ("I", "I;16BS"),
(II, 0, (3,), 1, (32,), ()): ("F", "F;32F"),
(MM, 0, (3,), 1, (32,), ()): ("F", "F;32BF"),
(II, 1, (1,), 1, (32,), ()): ("I", "I;32N"),
(II, 1, (2,), 1, (32,), ()): ("I", "I;32S"),
- (MM, 1, (2,), 1, (32,), ()): ("I;32BS", "I;32BS"),
+ (MM, 1, (2,), 1, (32,), ()): ("I", "I;32BS"),
(II, 1, (3,), 1, (32,), ()): ("F", "F;32F"),
(MM, 1, (3,), 1, (32,), ()): ("F", "F;32BF"),
@@ -199,6 +202,10 @@ OPEN_INFO = {
(MM, 2, (1,), 1, (8, 8, 8, 8), ()): ("RGBA", "RGBA"), # missing ExtraSamples
(II, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
(MM, 2, (1,), 1, (8, 8, 8, 8), (0,)): ("RGBX", "RGBX"),
+ (II, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"),
+ (MM, 2, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("RGBX", "RGBXX"),
+ (II, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"),
+ (MM, 2, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("RGBX", "RGBXXX"),
(II, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
(MM, 2, (1,), 1, (8, 8, 8, 8), (1,)): ("RGBA", "RGBa"),
(II, 2, (1,), 1, (8, 8, 8, 8), (2,)): ("RGBA", "RGBA"),
@@ -206,6 +213,17 @@ OPEN_INFO = {
(II, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
(MM, 2, (1,), 1, (8, 8, 8, 8), (999,)): ("RGBA", "RGBA"), # Corel Draw 10
+ (II, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16L"),
+ (MM, 2, (1,), 1, (16, 16, 16), ()): ("RGB", "RGB;16B"),
+ (II, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16L"),
+ (MM, 2, (1,), 1, (16, 16, 16, 16), ()): ("RGBA", "RGBA;16B"),
+ (II, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16L"),
+ (MM, 2, (1,), 1, (16, 16, 16, 16), (0,)): ("RGBX", "RGBX;16B"),
+ (II, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16L"),
+ (MM, 2, (1,), 1, (16, 16, 16, 16), (1,)): ("RGBA", "RGBa;16B"),
+ (II, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16L"),
+ (MM, 2, (1,), 1, (16, 16, 16, 16), (2,)): ("RGBA", "RGBA;16B"),
+
(II, 3, (1,), 1, (1,), ()): ("P", "P;1"),
(MM, 3, (1,), 1, (1,), ()): ("P", "P;1"),
(II, 3, (1,), 2, (1,), ()): ("P", "P;1R"),
@@ -227,15 +245,30 @@ OPEN_INFO = {
(II, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
(MM, 5, (1,), 1, (8, 8, 8, 8), ()): ("CMYK", "CMYK"),
+ (II, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
+ (MM, 5, (1,), 1, (8, 8, 8, 8, 8), (0,)): ("CMYK", "CMYKX"),
+ (II, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
+ (MM, 5, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0)): ("CMYK", "CMYKXX"),
(II, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
(MM, 6, (1,), 1, (8, 8, 8), ()): ("YCbCr", "YCbCr"),
+ (II, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
+ (MM, 6, (1,), 1, (8, 8, 8, 8), (0,)): ("YCbCr", "YCbCrX"),
+ (II, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
+ (MM, 6, (1,), 1, (8, 8, 8, 8, 8), (0, 0)): ("YCbCr", "YCbCrXXX"),
+ (II, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
+ (MM, 6, (1,), 1, (8, 8, 8, 8, 8, 8), (0, 0, 0)): ("YCbCr", "YCbCrXXX"),
(II, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
(MM, 8, (1,), 1, (8, 8, 8), ()): ("LAB", "LAB"),
}
-PREFIXES = [b"MM\000\052", b"II\052\000", b"II\xBC\000"]
+PREFIXES = [
+ b"MM\x00\x2A", # Valid TIFF header with big-endian byte order
+ b"II\x2A\x00", # Valid TIFF header with little-endian byte order
+ b"MM\x2A\x00", # Invalid TIFF header, assume big-endian
+ b"II\x00\x2A", # Invalid TIFF header, assume little-endian
+]
def _accept(prefix):
@@ -247,6 +280,7 @@ def _limit_rational(val, max_val):
n_d = IFDRational(1 / val if inv else val).limit_rational(max_val)
return n_d[::-1] if inv else n_d
+
##
# Wrapper for TIFF IFDs.
@@ -278,12 +312,12 @@ class IFDRational(Rational):
self._numerator = value
self._val = float(1)
- if type(value) == Fraction:
+ if isinstance(value, Fraction):
self._numerator = value.numerator
self._denominator = value.denominator
self._val = value
- if type(value) == IFDRational:
+ if isinstance(value, IFDRational):
self._denominator = value.denominator
self._numerator = value.numerator
self._val = value._val
@@ -294,11 +328,7 @@ class IFDRational(Rational):
return
elif denominator == 1:
- if sys.hexversion < 0x2070000 and type(value) == float:
- # python 2.6 is different.
- self._val = Fraction.from_float(value)
- else:
- self._val = Fraction(value)
+ self._val = Fraction(value)
else:
self._val = Fraction(value, denominator)
@@ -342,7 +372,7 @@ class IFDRational(Rational):
'rfloordiv','mod','rmod', 'pow','rpow', 'pos', 'neg',
'abs', 'trunc', 'lt', 'gt', 'le', 'ge', 'nonzero',
'ceil', 'floor', 'round']
- print "\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a)
+ print("\n".join("__%s__ = _delegate('__%s__')" % (s,s) for s in a))
"""
__add__ = _delegate('__add__')
@@ -375,7 +405,7 @@ class IFDRational(Rational):
__round__ = _delegate('__round__')
-class ImageFileDirectory_v2(collections.MutableMapping):
+class ImageFileDirectory_v2(MutableMapping):
"""This class represents a TIFF tag directory. To speed things up, we
don't decode tags unless they're asked for.
@@ -471,15 +501,6 @@ class ImageFileDirectory_v2(collections.MutableMapping):
def __str__(self):
return str(dict(self))
- def as_dict(self):
- """Return a dictionary of the image's tags.
-
- .. deprecated:: 3.0.0
- """
- warnings.warn("as_dict() is deprecated. " +
- "Please use dict(ifd) instead.", DeprecationWarning)
- return dict(self)
-
def named(self):
"""
:returns: dict of name|key: value
@@ -506,7 +527,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
def __contains__(self, tag):
return tag in self._tags_v2 or tag in self._tagdata
- if bytes is str:
+ if not py3:
def has_key(self, tag):
return tag in self
@@ -515,7 +536,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
def _setitem(self, tag, value, legacy_api):
basetypes = (Number, bytes, str)
- if bytes is str:
+ if not py3:
basetypes += unicode,
info = TiffTags.lookup(tag)
@@ -536,25 +557,43 @@ class ImageFileDirectory_v2(collections.MutableMapping):
elif all(isinstance(v, float) for v in values):
self.tagtype[tag] = 12
else:
- if bytes is str:
- # Never treat data as binary by default on Python 2.
- self.tagtype[tag] = 2
- else:
+ if py3:
if all(isinstance(v, str) for v in values):
self.tagtype[tag] = 2
+ else:
+ # Never treat data as binary by default on Python 2.
+ self.tagtype[tag] = 2
- if self.tagtype[tag] == 7 and bytes is not str:
- values = [value.encode("ascii", 'replace') if isinstance(value, str) else value]
+ if self.tagtype[tag] == 7 and py3:
+ values = [value.encode("ascii", 'replace') if isinstance(
+ value, str) else value]
values = tuple(info.cvt_enum(value) for value in values)
dest = self._tags_v1 if legacy_api else self._tags_v2
- if info.length == 1:
- if legacy_api and self.tagtype[tag] in [5, 10]:
+ # Three branches:
+ # Spec'd length == 1, Actual length 1, store as element
+ # Spec'd length == 1, Actual > 1, Warn and truncate. Formerly barfed.
+ # No Spec, Actual length 1, Formerly (<4.2) returned a 1 element tuple.
+ # Don't mess with the legacy api, since it's frozen.
+ if ((info.length == 1) or
+ (info.length is None and len(values) == 1 and not legacy_api)):
+ # Don't mess with the legacy api, since it's frozen.
+ if legacy_api and self.tagtype[tag] in [5, 10]: # rationals
values = values,
- dest[tag], = values
+ try:
+ dest[tag], = values
+ except ValueError:
+ # We've got a builtin tag with 1 expected entry
+ warnings.warn(
+ "Metadata Warning, tag %s had too many entries: %s, expected 1" % (
+ tag, len(values)))
+ dest[tag] = values[0]
+
else:
+ # Spec'd length > 1 or undefined
+ # Unspec'd, and length > 1
dest[tag] = values
def __delitem__(self, tag):
@@ -573,7 +612,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
def _register_loader(idx, size):
def decorator(func):
- from PIL.TiffTags import TYPES
+ from .TiffTags import TYPES
if func.__name__.startswith("load_"):
TYPES[idx] = func.__name__[5:].replace("_", " ")
_load_dispatch[idx] = size, func
@@ -587,19 +626,23 @@ class ImageFileDirectory_v2(collections.MutableMapping):
return decorator
def _register_basic(idx_fmt_name):
- from PIL.TiffTags import TYPES
+ from .TiffTags import TYPES
idx, fmt, name = idx_fmt_name
TYPES[idx] = name
size = struct.calcsize("=" + fmt)
_load_dispatch[idx] = size, lambda self, data, legacy_api=True: (
- self._unpack("{0}{1}".format(len(data) // size, fmt), data))
+ self._unpack("{}{}".format(len(data) // size, fmt), data))
_write_dispatch[idx] = lambda self, *values: (
b"".join(self._pack(fmt, value) for value in values))
list(map(_register_basic,
- [(3, "H", "short"), (4, "L", "long"),
- (6, "b", "signed byte"), (8, "h", "signed short"),
- (9, "l", "signed long"), (11, "f", "float"), (12, "d", "double")]))
+ [(3, "H", "short"),
+ (4, "L", "long"),
+ (6, "b", "signed byte"),
+ (8, "h", "signed short"),
+ (9, "l", "signed long"),
+ (11, "f", "float"),
+ (12, "d", "double")]))
@_register_loader(1, 1) # Basic type, except for the legacy API.
def load_byte(self, data, legacy_api=True):
@@ -618,14 +661,15 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_writer(2)
def write_string(self, value):
# remerge of https://github.com/python-pillow/Pillow/pull/1416
- if sys.version_info[0] == 2:
+ if sys.version_info.major == 2:
value = value.decode('ascii', 'replace')
return b"" + value.encode('ascii', 'replace') + b"\0"
@_register_loader(5, 8)
def load_rational(self, data, legacy_api=True):
- vals = self._unpack("{0}L".format(len(data) // 4), data)
- combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
+ vals = self._unpack("{}L".format(len(data) // 4), data)
+
+ def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2]))
@@ -644,8 +688,9 @@ class ImageFileDirectory_v2(collections.MutableMapping):
@_register_loader(10, 8)
def load_signed_rational(self, data, legacy_api=True):
- vals = self._unpack("{0}l".format(len(data) // 4), data)
- combine = lambda a, b: (a, b) if legacy_api else IFDRational(a, b)
+ vals = self._unpack("{}l".format(len(data) // 4), data)
+
+ def combine(a, b): return (a, b) if legacy_api else IFDRational(a, b)
return tuple(combine(num, denom)
for num, denom in zip(vals[::2], vals[1::2]))
@@ -669,7 +714,8 @@ class ImageFileDirectory_v2(collections.MutableMapping):
try:
for i in range(self._unpack("H", self._ensure_read(fp, 2))[0]):
- tag, typ, count, data = self._unpack("HHL4s", self._ensure_read(fp, 12))
+ tag, typ, count, data = self._unpack("HHL4s",
+ self._ensure_read(fp, 12))
if DEBUG:
tagname = TiffTags.lookup(tag).name
typname = TYPES.get(typ, "unknown")
@@ -697,8 +743,11 @@ class ImageFileDirectory_v2(collections.MutableMapping):
if len(data) != size:
warnings.warn("Possibly corrupt EXIF data. "
- "Expecting to read %d bytes but only got %d. "
- "Skipping tag %s" % (size, len(data), tag))
+ "Expecting to read %d bytes but only got %d."
+ " Skipping tag %s" % (size, len(data), tag))
+ continue
+
+ if not data:
continue
self._tagdata[tag] = data
@@ -754,7 +803,8 @@ class ImageFileDirectory_v2(collections.MutableMapping):
if len(data) <= 4:
entries.append((tag, typ, count, data.ljust(4, b"\0"), b""))
else:
- entries.append((tag, typ, count, self._pack("L", offset), data))
+ entries.append((tag, typ, count, self._pack("L", offset),
+ data))
offset += (len(data) + 1) // 2 * 2 # pad to word
# update strip offset data to point beyond auxiliary data
@@ -783,6 +833,7 @@ class ImageFileDirectory_v2(collections.MutableMapping):
return offset
+
ImageFileDirectory_v2._load_dispatch = _load_dispatch
ImageFileDirectory_v2._write_dispatch = _write_dispatch
for idx, name in TYPES.items():
@@ -801,7 +852,7 @@ class ImageFileDirectory_v1(ImageFileDirectory_v2):
ifd = ImageFileDirectory_v1()
ifd[key] = 'Some Data'
ifd.tagtype[key] = 2
- print ifd[key]
+ print(ifd[key])
('Some Data',)
Also contains a dictionary of tag types as read from the tiff image file,
@@ -890,6 +941,7 @@ class TiffImageFile(ImageFile.ImageFile):
format = "TIFF"
format_description = "Adobe TIFF"
+ _close_exclusive_fp_after_loading = False
def _open(self):
"Open the first image in a TIFF file"
@@ -934,20 +986,25 @@ class TiffImageFile(ImageFile.ImageFile):
@property
def is_animated(self):
if self._is_animated is None:
- current = self.tell()
+ if self._n_frames is not None:
+ self._is_animated = self._n_frames != 1
+ else:
+ current = self.tell()
- try:
- self.seek(1)
- self._is_animated = True
- except EOFError:
- self._is_animated = False
+ try:
+ self.seek(1)
+ self._is_animated = True
+ except EOFError:
+ self._is_animated = False
- self.seek(current)
+ self.seek(current)
return self._is_animated
def seek(self, frame):
"Select a given frame as current image"
- self._seek(max(frame, 0)) # Questionable backwards compatibility.
+ if not self._seek_check(frame):
+ return
+ self._seek(frame)
# Create a new core image object on second and
# subsequent frames in the image. Image may be
# different size/mode.
@@ -975,6 +1032,7 @@ class TiffImageFile(ImageFile.ImageFile):
self.__frame += 1
self.fp.seek(self._frame_pos[frame])
self.tag_v2.load(self.fp)
+ self.__next = self.tag_v2.next
# fill the legacy tag/ifd entries
self.tag = self.ifd = ImageFileDirectory_v1.from_v2(self.tag_v2)
self.__frame = frame
@@ -993,22 +1051,8 @@ class TiffImageFile(ImageFile.ImageFile):
compression = self._compression
if compression == "raw":
args = (rawmode, 0, 1)
- elif compression == "jpeg":
- args = rawmode, ""
- if JPEGTABLES in self.tag_v2:
- # Hack to handle abbreviated JPEG headers
- # FIXME This will fail with more than one value
- self.tile_prefix, = self.tag_v2[JPEGTABLES]
elif compression == "packbits":
args = rawmode
- elif compression == "tiff_lzw":
- args = rawmode
- if PREDICTOR in self.tag_v2:
- # Section 14: Differencing Predictor
- self.decoderconfig = (self.tag_v2[PREDICTOR],)
-
- if ICCPROFILE in self.tag_v2:
- self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
return args
@@ -1017,6 +1061,12 @@ class TiffImageFile(ImageFile.ImageFile):
return self._load_libtiff()
return super(TiffImageFile, self).load()
+ def load_end(self):
+ # allow closing if we're on the first frame, there's no next
+ # This is the ImageFile.load path only, libtiff specific below.
+ if self.__frame == 0 and not self.__next:
+ self._close_exclusive_fp_after_loading = True
+
def _load_libtiff(self):
""" Overload method triggered when we detect a compressed tiff
Calls out to libtiff """
@@ -1036,8 +1086,28 @@ class TiffImageFile(ImageFile.ImageFile):
# (self._compression, (extents tuple),
# 0, (rawmode, self._compression, fp))
extents = self.tile[0][1]
- args = self.tile[0][3] + (self.tag_v2.offset,)
- decoder = Image._getdecoder(self.mode, 'libtiff', args,
+ args = list(self.tile[0][3]) + [self.tag_v2.offset]
+
+ # To be nice on memory footprint, if there's a
+ # file descriptor, use that instead of reading
+ # into a string in python.
+ # libtiff closes the file descriptor, so pass in a dup.
+ try:
+ fp = hasattr(self.fp, "fileno") and os.dup(self.fp.fileno())
+ # flush the file descriptor, prevents error on pypy 2.4+
+ # should also eliminate the need for fp.tell for py3
+ # in _seek
+ if hasattr(self.fp, "flush"):
+ self.fp.flush()
+ except IOError:
+ # io.BytesIO have a fileno, but returns an IOError if
+ # it doesn't use a file descriptor.
+ fp = False
+
+ if fp:
+ args[2] = fp
+
+ decoder = Image._getdecoder(self.mode, 'libtiff', tuple(args),
self.decoderconfig)
try:
decoder.setimage(self.im, extents)
@@ -1074,16 +1144,14 @@ class TiffImageFile(ImageFile.ImageFile):
self.tile = []
self.readonly = 0
# libtiff closed the fp in a, we need to close self.fp, if possible
- if hasattr(self.fp, 'close'):
- if not self.__next:
+ if self._exclusive_fp:
+ if self.__frame == 0 and not self.__next:
self.fp.close()
- self.fp = None # might be shared
+ self.fp = None # might be shared
if err < 0:
raise IOError(err)
- self.load_end()
-
return Image.Image.load(self)
def _setup(self):
@@ -1119,7 +1187,7 @@ class TiffImageFile(ImageFile.ImageFile):
sampleFormat = self.tag_v2.get(SAMPLEFORMAT, (1,))
if (len(sampleFormat) > 1
- and max(sampleFormat) == min(sampleFormat) == 1):
+ and max(sampleFormat) == min(sampleFormat) == 1):
# SAMPLEFORMAT is properly per band, so an RGB image will
# be (1,1,1). But, we don't support per band pixel types,
# and anything more than one band is a uint8. So, just
@@ -1127,12 +1195,23 @@ class TiffImageFile(ImageFile.ImageFile):
# for more exotic images.
sampleFormat = (1,)
+ bps_tuple = self.tag_v2.get(BITSPERSAMPLE, (1,))
+ extra_tuple = self.tag_v2.get(EXTRASAMPLES, ())
+ if photo in (2, 6, 8): # RGB, YCbCr, LAB
+ bps_count = 3
+ elif photo == 5: # CMYK
+ bps_count = 4
+ else:
+ bps_count = 1
+ bps_count += len(extra_tuple)
+ # Some files have only one value in bps_tuple,
+ # while should have more. Fix it
+ if bps_count > len(bps_tuple) and len(bps_tuple) == 1:
+ bps_tuple = bps_tuple * bps_count
+
# mode: check photometric interpretation and bits per pixel
- key = (
- self.tag_v2.prefix, photo, sampleFormat, fillorder,
- self.tag_v2.get(BITSPERSAMPLE, (1,)),
- self.tag_v2.get(EXTRASAMPLES, ())
- )
+ key = (self.tag_v2.prefix, photo, sampleFormat, fillorder,
+ bps_tuple, extra_tuple)
if DEBUG:
print("format key:", key)
try:
@@ -1152,11 +1231,16 @@ class TiffImageFile(ImageFile.ImageFile):
yres = self.tag_v2.get(Y_RESOLUTION, 1)
if xres and yres:
- resunit = self.tag_v2.get(RESOLUTION_UNIT, 1)
+ resunit = self.tag_v2.get(RESOLUTION_UNIT)
if resunit == 2: # dots per inch
self.info["dpi"] = xres, yres
elif resunit == 3: # dots per centimeter. convert to dpi
self.info["dpi"] = xres * 2.54, yres * 2.54
+ elif resunit is None: # used to default to 1, but now 2)
+ self.info["dpi"] = xres, yres
+ # For backward compatibility,
+ # we also preserve the old behavior
+ self.info["resolution"] = xres, yres
else: # No absolute unit of measurement
self.info["resolution"] = xres, yres
@@ -1169,16 +1253,9 @@ class TiffImageFile(ImageFile.ImageFile):
offsets = self.tag_v2[STRIPOFFSETS]
h = self.tag_v2.get(ROWSPERSTRIP, ysize)
w = self.size[0]
- if READ_LIBTIFF or self._compression in ["tiff_ccitt", "group3",
- "group4", "tiff_jpeg",
- "tiff_adobe_deflate",
- "tiff_thunderscan",
- "tiff_deflate",
- "tiff_sgilog",
- "tiff_sgilog24",
- "tiff_raw_16"]:
+ if READ_LIBTIFF or self._compression != 'raw':
# if DEBUG:
- # print "Activating g4 compression for whole file"
+ # print("Activating g4 compression for whole file")
# Decoder expects entire file as one tile.
# There's a buffer size limit in load (64k)
@@ -1190,24 +1267,6 @@ class TiffImageFile(ImageFile.ImageFile):
self.use_load_libtiff = True
- # To be nice on memory footprint, if there's a
- # file descriptor, use that instead of reading
- # into a string in python.
-
- # libtiff closes the file descriptor, so pass in a dup.
- try:
- fp = hasattr(self.fp, "fileno") and \
- os.dup(self.fp.fileno())
- # flush the file descriptor, prevents error on pypy 2.4+
- # should also eliminate the need for fp.tell for py3
- # in _seek
- if hasattr(self.fp, "flush"):
- self.fp.flush()
- except IOError:
- # io.BytesIO have a fileno, but returns an IOError if
- # it doesn't use a file descriptor.
- fp = False
-
# libtiff handles the fillmode for us, so 1;IR should
# actually be 1;I. Including the R double reverses the
# bits, so stripes of the image are reversed. See
@@ -1228,12 +1287,16 @@ class TiffImageFile(ImageFile.ImageFile):
# we're expecting image byte order. So, if the rawmode
# contains I;16, we need to convert from native to image
# byte order.
- if self.mode in ('I;16B', 'I;16') and 'I;16' in rawmode:
+ if rawmode == 'I;16':
rawmode = 'I;16N'
+ if ';16B' in rawmode:
+ rawmode = rawmode.replace(';16B', ';16N')
+ if ';16L' in rawmode:
+ rawmode = rawmode.replace(';16L', ';16N')
# Offset in the tile tuple is 0, we go from 0,0 to
# w,h, and we only do this once -- eds
- a = (rawmode, self._compression, fp)
+ a = (rawmode, self._compression, False)
self.tile.append(
(self._compression,
(0, 0, w, ysize),
@@ -1241,12 +1304,12 @@ class TiffImageFile(ImageFile.ImageFile):
a = None
else:
- for i in range(len(offsets)):
+ for i, offset in enumerate(offsets):
a = self._decoder(rawmode, l, i)
self.tile.append(
(self._compression,
(0, min(y, ysize), w, min(y+h, ysize)),
- offsets[i], a))
+ offset, a))
if DEBUG:
print("tiles: ", self.tile)
y = y + h
@@ -1280,11 +1343,17 @@ class TiffImageFile(ImageFile.ImageFile):
print("- unsupported data organization")
raise SyntaxError("unknown data organization")
+ # Fix up info.
+ if ICCPROFILE in self.tag_v2:
+ self.info['icc_profile'] = self.tag_v2[ICCPROFILE]
+
# fixup palette descriptor
if self.mode == "P":
palette = [o8(b // 256) for b in self.tag_v2[COLORMAP]]
self.palette = ImagePalette.raw("RGB;L", b"".join(palette))
+
+
#
# --------------------------------------------------------------------
# Write TIFF files
@@ -1359,12 +1428,12 @@ def _save(im, fp, filename):
IPTC_NAA_CHUNK, PHOTOSHOP_CHUNK, XMP):
if key in im.tag_v2:
ifd[key] = im.tag_v2[key]
- ifd.tagtype[key] = im.tag_v2.tagtype.get(key, None)
+ ifd.tagtype[key] = im.tag_v2.tagtype[key]
- # preserve ICC profile (should also work when saving other formats
- # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
- if "icc_profile" in im.info:
- ifd[ICCPROFILE] = im.info["icc_profile"]
+ # preserve ICC profile (should also work when saving other formats
+ # which support profiles as TIFF) -- 2008-06-06 Florian Hoech
+ if "icc_profile" in im.info:
+ ifd[ICCPROFILE] = im.info["icc_profile"]
for key, name in [(IMAGEDESCRIPTION, "description"),
(X_RESOLUTION, "resolution"),
@@ -1376,11 +1445,6 @@ def _save(im, fp, filename):
(DATE_TIME, "date_time"),
(ARTIST, "artist"),
(COPYRIGHT, "copyright")]:
- name_with_spaces = name.replace("_", " ")
- if "_" in name and name_with_spaces in im.encoderinfo:
- warnings.warn("%r is deprecated; use %r instead" %
- (name_with_spaces, name), DeprecationWarning)
- ifd[key] = im.encoderinfo[name.replace("_", " ")]
if name in im.encoderinfo:
ifd[key] = im.encoderinfo[name]
@@ -1404,7 +1468,6 @@ def _save(im, fp, filename):
if im.mode == "P":
lut = im.im.getpalette("RGB", "RGB;L")
ifd[COLORMAP] = tuple(i8(v) * 256 for v in lut)
-
# data orientation
stride = len(bits) * ((im.size[0]*bits[0]+7)//8)
ifd[ROWSPERSTRIP] = im.size[1]
@@ -1448,7 +1511,7 @@ def _save(im, fp, filename):
if tag not in TiffTags.LIBTIFF_CORE:
continue
if tag not in atts and tag not in blocklist:
- if isinstance(value, unicode if bytes is str else str):
+ if isinstance(value, str if py3 else unicode):
atts[tag] = value.encode('ascii', 'replace') + b"\0"
elif isinstance(value, IFDRational):
atts[tag] = float(value)
@@ -1491,14 +1554,285 @@ def _save(im, fp, filename):
# just to access o32 and o16 (using correct byte order)
im._debug_multipage = ifd
+
+class AppendingTiffWriter:
+ fieldSizes = [
+ 0, # None
+ 1, # byte
+ 1, # ascii
+ 2, # short
+ 4, # long
+ 8, # rational
+ 1, # sbyte
+ 1, # undefined
+ 2, # sshort
+ 4, # slong
+ 8, # srational
+ 4, # float
+ 8, # double
+ ]
+
+ # StripOffsets = 273
+ # FreeOffsets = 288
+ # TileOffsets = 324
+ # JPEGQTables = 519
+ # JPEGDCTables = 520
+ # JPEGACTables = 521
+ Tags = {273, 288, 324, 519, 520, 521}
+
+ def __init__(self, fn, new=False):
+ if hasattr(fn, 'read'):
+ self.f = fn
+ self.close_fp = False
+ else:
+ self.name = fn
+ self.close_fp = True
+ try:
+ self.f = io.open(fn, "w+b" if new else "r+b")
+ except IOError:
+ self.f = io.open(fn, "w+b")
+ self.beginning = self.f.tell()
+ self.setup()
+
+ def setup(self):
+ # Reset everything.
+ self.f.seek(self.beginning, os.SEEK_SET)
+
+ self.whereToWriteNewIFDOffset = None
+ self.offsetOfNewPage = 0
+
+ self.IIMM = IIMM = self.f.read(4)
+ if not IIMM:
+ # empty file - first page
+ self.isFirst = True
+ return
+
+ self.isFirst = False
+ if IIMM == b"II\x2a\x00":
+ self.setEndian("<")
+ elif IIMM == b"MM\x00\x2a":
+ self.setEndian(">")
+ else:
+ raise RuntimeError("Invalid TIFF file header")
+
+ self.skipIFDs()
+ self.goToEnd()
+
+ def finalize(self):
+ if self.isFirst:
+ return
+
+ # fix offsets
+ self.f.seek(self.offsetOfNewPage)
+
+ IIMM = self.f.read(4)
+ if not IIMM:
+ # raise RuntimeError("nothing written into new page")
+ # Make it easy to finish a frame without committing to a new one.
+ return
+
+ if IIMM != self.IIMM:
+ raise RuntimeError("IIMM of new page doesn't match IIMM of "
+ "first page")
+
+ IFDoffset = self.readLong()
+ IFDoffset += self.offsetOfNewPage
+ self.f.seek(self.whereToWriteNewIFDOffset)
+ self.writeLong(IFDoffset)
+ self.f.seek(IFDoffset)
+ self.fixIFD()
+
+ def newFrame(self):
+ # Call this to finish a frame.
+ self.finalize()
+ self.setup()
+
+ def __enter__(self):
+ return self
+
+ def __exit__(self, exc_type, exc_value, traceback):
+ if self.close_fp:
+ self.close()
+ return False
+
+ def tell(self):
+ return self.f.tell() - self.offsetOfNewPage
+
+ def seek(self, offset, whence):
+ if whence == os.SEEK_SET:
+ offset += self.offsetOfNewPage
+
+ self.f.seek(offset, whence)
+ return self.tell()
+
+ def goToEnd(self):
+ self.f.seek(0, os.SEEK_END)
+ pos = self.f.tell()
+
+ # pad to 16 byte boundary
+ padBytes = 16 - pos % 16
+ if 0 < padBytes < 16:
+ self.f.write(bytes(bytearray(padBytes)))
+ self.offsetOfNewPage = self.f.tell()
+
+ def setEndian(self, endian):
+ self.endian = endian
+ self.longFmt = self.endian + "L"
+ self.shortFmt = self.endian + "H"
+ self.tagFormat = self.endian + "HHL"
+
+ def skipIFDs(self):
+ while True:
+ IFDoffset = self.readLong()
+ if IFDoffset == 0:
+ self.whereToWriteNewIFDOffset = self.f.tell() - 4
+ break
+
+ self.f.seek(IFDoffset)
+ numTags = self.readShort()
+ self.f.seek(numTags * 12, os.SEEK_CUR)
+
+ def write(self, data):
+ return self.f.write(data)
+
+ def readShort(self):
+ value, = struct.unpack(self.shortFmt, self.f.read(2))
+ return value
+
+ def readLong(self):
+ value, = struct.unpack(self.longFmt, self.f.read(4))
+ return value
+
+ def rewriteLastShortToLong(self, value):
+ self.f.seek(-2, os.SEEK_CUR)
+ bytesWritten = self.f.write(struct.pack(self.longFmt, value))
+ if bytesWritten is not None and bytesWritten != 4:
+ raise RuntimeError("wrote only %u bytes but wanted 4" %
+ bytesWritten)
+
+ def rewriteLastShort(self, value):
+ self.f.seek(-2, os.SEEK_CUR)
+ bytesWritten = self.f.write(struct.pack(self.shortFmt, value))
+ if bytesWritten is not None and bytesWritten != 2:
+ raise RuntimeError("wrote only %u bytes but wanted 2" %
+ bytesWritten)
+
+ def rewriteLastLong(self, value):
+ self.f.seek(-4, os.SEEK_CUR)
+ bytesWritten = self.f.write(struct.pack(self.longFmt, value))
+ if bytesWritten is not None and bytesWritten != 4:
+ raise RuntimeError("wrote only %u bytes but wanted 4" %
+ bytesWritten)
+
+ def writeShort(self, value):
+ bytesWritten = self.f.write(struct.pack(self.shortFmt, value))
+ if bytesWritten is not None and bytesWritten != 2:
+ raise RuntimeError("wrote only %u bytes but wanted 2" %
+ bytesWritten)
+
+ def writeLong(self, value):
+ bytesWritten = self.f.write(struct.pack(self.longFmt, value))
+ if bytesWritten is not None and bytesWritten != 4:
+ raise RuntimeError("wrote only %u bytes but wanted 4" %
+ bytesWritten)
+
+ def close(self):
+ self.finalize()
+ self.f.close()
+
+ def fixIFD(self):
+ numTags = self.readShort()
+
+ for i in range(numTags):
+ tag, fieldType, count = struct.unpack(self.tagFormat,
+ self.f.read(8))
+
+ fieldSize = self.fieldSizes[fieldType]
+ totalSize = fieldSize * count
+ isLocal = (totalSize <= 4)
+ if not isLocal:
+ offset = self.readLong()
+ offset += self.offsetOfNewPage
+ self.rewriteLastLong(offset)
+
+ if tag in self.Tags:
+ curPos = self.f.tell()
+
+ if isLocal:
+ self.fixOffsets(count, isShort=(fieldSize == 2),
+ isLong=(fieldSize == 4))
+ self.f.seek(curPos + 4)
+ else:
+ self.f.seek(offset)
+ self.fixOffsets(count, isShort=(fieldSize == 2),
+ isLong=(fieldSize == 4))
+ self.f.seek(curPos)
+
+ offset = curPos = None
+
+ elif isLocal:
+ # skip the locally stored value that is not an offset
+ self.f.seek(4, os.SEEK_CUR)
+
+ def fixOffsets(self, count, isShort=False, isLong=False):
+ if not isShort and not isLong:
+ raise RuntimeError("offset is neither short nor long")
+
+ for i in range(count):
+ offset = self.readShort() if isShort else self.readLong()
+ offset += self.offsetOfNewPage
+ if isShort and offset >= 65536:
+ # offset is now too large - we must convert shorts to longs
+ if count != 1:
+ raise RuntimeError("not implemented") # XXX TODO
+
+ # simple case - the offset is just one and therefore it is
+ # local (not referenced with another offset)
+ self.rewriteLastShortToLong(offset)
+ self.f.seek(-10, os.SEEK_CUR)
+ self.writeShort(4) # rewrite the type to LONG
+ self.f.seek(8, os.SEEK_CUR)
+ elif isShort:
+ self.rewriteLastShort(offset)
+ else:
+ self.rewriteLastLong(offset)
+
+
+def _save_all(im, fp, filename):
+ encoderinfo = im.encoderinfo.copy()
+ encoderconfig = im.encoderconfig
+ append_images = list(encoderinfo.get("append_images", []))
+ if not hasattr(im, "n_frames") and not append_images:
+ return _save(im, fp, filename)
+
+ cur_idx = im.tell()
+ try:
+ with AppendingTiffWriter(fp) as tf:
+ for ims in [im]+append_images:
+ ims.encoderinfo = encoderinfo
+ ims.encoderconfig = encoderconfig
+ if not hasattr(ims, "n_frames"):
+ nfr = 1
+ else:
+ nfr = ims.n_frames
+
+ for idx in range(nfr):
+ ims.seek(idx)
+ ims.load()
+ _save(ims, tf, filename)
+ tf.newFrame()
+ finally:
+ im.seek(cur_idx)
+
+
#
# --------------------------------------------------------------------
# Register
Image.register_open(TiffImageFile.format, TiffImageFile, _accept)
Image.register_save(TiffImageFile.format, _save)
+Image.register_save_all(TiffImageFile.format, _save_all)
-Image.register_extension(TiffImageFile.format, ".tif")
-Image.register_extension(TiffImageFile.format, ".tiff")
+Image.register_extensions(TiffImageFile.format, [".tif", ".tiff"])
Image.register_mime(TiffImageFile.format, "image/tiff")
diff --git a/server/www/packages/packages-windows/x86/PIL/TiffTags.py b/server/www/packages/packages-windows/x86/PIL/TiffTags.py
index ecc63ba..427f3a4 100644
--- a/server/www/packages/packages-windows/x86/PIL/TiffTags.py
+++ b/server/www/packages/packages-windows/x86/PIL/TiffTags.py
@@ -23,7 +23,7 @@ from collections import namedtuple
class TagInfo(namedtuple("_TagInfo", "value name type length enum")):
__slots__ = []
- def __new__(cls, value=None, name="unknown", type=None, length=0, enum=None):
+ def __new__(cls, value=None, name="unknown", type=None, length=None, enum=None):
return super(TagInfo, cls).__new__(
cls, value, name, type, length, enum or {})
@@ -142,6 +142,8 @@ TAGS_V2 = {
341: ("SMaxSampleValue", DOUBLE, 0),
342: ("TransferRange", SHORT, 6),
+ 347: ("JPEGTables", UNDEFINED, 1),
+
# obsolete JPEG tags
512: ("JPEGProc", SHORT, 1),
513: ("JPEGInterchangeFormat", LONG, 1),
@@ -158,7 +160,10 @@ TAGS_V2 = {
531: ("YCbCrPositioning", SHORT, 1),
532: ("ReferenceBlackWhite", LONG, 0),
+ 700: ('XMP', BYTE, 1),
+
33432: ("Copyright", ASCII, 1),
+ 34377: ('PhotoshopInfo', BYTE, 1),
# FIXME add more tags here
34665: ("ExifIFD", SHORT, 1),
@@ -169,7 +174,7 @@ TAGS_V2 = {
45056: ("MPFVersion", UNDEFINED, 1),
45057: ("NumberOfImages", LONG, 1),
45058: ("MPEntry", UNDEFINED, 1),
- 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check
+ 45059: ("ImageUIDList", UNDEFINED, 0), # UNDONE, check
45060: ("TotalFrames", LONG, 1),
45313: ("MPIndividualNum", LONG, 1),
45569: ("PanOrientation", LONG, 1),
@@ -188,8 +193,8 @@ TAGS_V2 = {
50741: ("MakerNoteSafety", SHORT, 1, {"Unsafe": 0, "Safe": 1}),
50780: ("BestQualityScale", RATIONAL, 1),
- 50838: ("ImageJMetaDataByteCounts", LONG, 1),
- 50839: ("ImageJMetaData", UNDEFINED, 1)
+ 50838: ("ImageJMetaDataByteCounts", LONG, 0), # Can be more than one
+ 50839: ("ImageJMetaData", UNDEFINED, 1) # see Issue #2006
}
# Legacy Tags structure
@@ -350,6 +355,7 @@ def _populate():
TAGS_V2[k] = TagInfo(k, *v)
+
_populate()
##
# Map type numbers to type names -- defined in ImageFileDirectory.
@@ -377,54 +383,54 @@ TYPES = {}
# adding to the custom dictionary. From tif_dir.c, searching for
# case TIFFTAG in the _TIFFVSetField function:
# Line: item.
-# 148: case TIFFTAG_SUBFILETYPE:
-# 151: case TIFFTAG_IMAGEWIDTH:
-# 154: case TIFFTAG_IMAGELENGTH:
-# 157: case TIFFTAG_BITSPERSAMPLE:
-# 181: case TIFFTAG_COMPRESSION:
-# 202: case TIFFTAG_PHOTOMETRIC:
-# 205: case TIFFTAG_THRESHHOLDING:
-# 208: case TIFFTAG_FILLORDER:
-# 214: case TIFFTAG_ORIENTATION:
-# 221: case TIFFTAG_SAMPLESPERPIXEL:
-# 228: case TIFFTAG_ROWSPERSTRIP:
-# 238: case TIFFTAG_MINSAMPLEVALUE:
-# 241: case TIFFTAG_MAXSAMPLEVALUE:
-# 244: case TIFFTAG_SMINSAMPLEVALUE:
-# 247: case TIFFTAG_SMAXSAMPLEVALUE:
-# 250: case TIFFTAG_XRESOLUTION:
-# 256: case TIFFTAG_YRESOLUTION:
-# 262: case TIFFTAG_PLANARCONFIG:
-# 268: case TIFFTAG_XPOSITION:
-# 271: case TIFFTAG_YPOSITION:
-# 274: case TIFFTAG_RESOLUTIONUNIT:
-# 280: case TIFFTAG_PAGENUMBER:
-# 284: case TIFFTAG_HALFTONEHINTS:
-# 288: case TIFFTAG_COLORMAP:
-# 294: case TIFFTAG_EXTRASAMPLES:
-# 298: case TIFFTAG_MATTEING:
-# 305: case TIFFTAG_TILEWIDTH:
-# 316: case TIFFTAG_TILELENGTH:
-# 327: case TIFFTAG_TILEDEPTH:
-# 333: case TIFFTAG_DATATYPE:
-# 344: case TIFFTAG_SAMPLEFORMAT:
-# 361: case TIFFTAG_IMAGEDEPTH:
-# 364: case TIFFTAG_SUBIFD:
-# 376: case TIFFTAG_YCBCRPOSITIONING:
-# 379: case TIFFTAG_YCBCRSUBSAMPLING:
-# 383: case TIFFTAG_TRANSFERFUNCTION:
-# 389: case TIFFTAG_REFERENCEBLACKWHITE:
-# 393: case TIFFTAG_INKNAMES:
+# 148: case TIFFTAG_SUBFILETYPE:
+# 151: case TIFFTAG_IMAGEWIDTH:
+# 154: case TIFFTAG_IMAGELENGTH:
+# 157: case TIFFTAG_BITSPERSAMPLE:
+# 181: case TIFFTAG_COMPRESSION:
+# 202: case TIFFTAG_PHOTOMETRIC:
+# 205: case TIFFTAG_THRESHHOLDING:
+# 208: case TIFFTAG_FILLORDER:
+# 214: case TIFFTAG_ORIENTATION:
+# 221: case TIFFTAG_SAMPLESPERPIXEL:
+# 228: case TIFFTAG_ROWSPERSTRIP:
+# 238: case TIFFTAG_MINSAMPLEVALUE:
+# 241: case TIFFTAG_MAXSAMPLEVALUE:
+# 244: case TIFFTAG_SMINSAMPLEVALUE:
+# 247: case TIFFTAG_SMAXSAMPLEVALUE:
+# 250: case TIFFTAG_XRESOLUTION:
+# 256: case TIFFTAG_YRESOLUTION:
+# 262: case TIFFTAG_PLANARCONFIG:
+# 268: case TIFFTAG_XPOSITION:
+# 271: case TIFFTAG_YPOSITION:
+# 274: case TIFFTAG_RESOLUTIONUNIT:
+# 280: case TIFFTAG_PAGENUMBER:
+# 284: case TIFFTAG_HALFTONEHINTS:
+# 288: case TIFFTAG_COLORMAP:
+# 294: case TIFFTAG_EXTRASAMPLES:
+# 298: case TIFFTAG_MATTEING:
+# 305: case TIFFTAG_TILEWIDTH:
+# 316: case TIFFTAG_TILELENGTH:
+# 327: case TIFFTAG_TILEDEPTH:
+# 333: case TIFFTAG_DATATYPE:
+# 344: case TIFFTAG_SAMPLEFORMAT:
+# 361: case TIFFTAG_IMAGEDEPTH:
+# 364: case TIFFTAG_SUBIFD:
+# 376: case TIFFTAG_YCBCRPOSITIONING:
+# 379: case TIFFTAG_YCBCRSUBSAMPLING:
+# 383: case TIFFTAG_TRANSFERFUNCTION:
+# 389: case TIFFTAG_REFERENCEBLACKWHITE:
+# 393: case TIFFTAG_INKNAMES:
# some of these are not in our TAGS_V2 dict and were included from tiff.h
-LIBTIFF_CORE = set([255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
- 278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
- 296, 297, 321, 320, 338, 32995, 322, 323, 32998,
- 32996, 339, 32997, 330, 531, 530, 301, 532, 333,
- # as above
- 269 # this has been in our tests forever, and works
- ])
+LIBTIFF_CORE = {255, 256, 257, 258, 259, 262, 263, 266, 274, 277,
+ 278, 280, 281, 340, 341, 282, 283, 284, 286, 287,
+ 296, 297, 321, 320, 338, 32995, 322, 323, 32998,
+ 32996, 339, 32997, 330, 531, 530, 301, 532, 333,
+ # as above
+ 269 # this has been in our tests forever, and works
+ }
LIBTIFF_CORE.remove(320) # Array of short, crashes
LIBTIFF_CORE.remove(301) # Array of short, crashes
diff --git a/server/www/packages/packages-windows/x86/PIL/WalImageFile.py b/server/www/packages/packages-windows/x86/PIL/WalImageFile.py
index 0cbd1ca..6602cc8 100644
--- a/server/www/packages/packages-windows/x86/PIL/WalImageFile.py
+++ b/server/www/packages/packages-windows/x86/PIL/WalImageFile.py
@@ -18,12 +18,11 @@
# the WalImageFile.open() function instead.
# This reader is based on the specification available from:
-# http://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
+# https://www.flipcode.com/archives/Quake_2_BSP_File_Format.shtml
# and has been tested with a few sample files found using google.
-from __future__ import print_function
-
-from PIL import Image, _binary
+from . import Image
+from ._binary import i32le as i32
try:
import builtins
@@ -31,48 +30,49 @@ except ImportError:
import __builtin__
builtins = __builtin__
-i32 = _binary.i32le
-
-
-##
-# Load texture from a Quake2 WAL texture file.
-#
-# By default, a Quake2 standard palette is attached to the texture.
-# To override the palette, use the putpalette method.
-#
-# @param filename WAL file name, or an opened file handle.
-# @return An image instance.
def open(filename):
+ """
+ Load texture from a Quake2 WAL texture file.
+
+ By default, a Quake2 standard palette is attached to the texture.
+ To override the palette, use the putpalette method.
+
+ :param filename: WAL file name, or an opened file handle.
+ :returns: An image instance.
+ """
# FIXME: modify to return a WalImageFile instance instead of
# plain Image object ?
+ def imopen(fp):
+ # read header fields
+ header = fp.read(32+24+32+12)
+ size = i32(header, 32), i32(header, 36)
+ offset = i32(header, 40)
+
+ # load pixel data
+ fp.seek(offset)
+
+ Image._decompression_bomb_check(size)
+ im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
+ im.putpalette(quake2palette)
+
+ im.format = "WAL"
+ im.format_description = "Quake2 Texture"
+
+ # strings are null-terminated
+ im.info["name"] = header[:32].split(b"\0", 1)[0]
+ next_name = header[56:56+32].split(b"\0", 1)[0]
+ if next_name:
+ im.info["next_name"] = next_name
+
+ return im
+
if hasattr(filename, "read"):
- fp = filename
+ return imopen(filename)
else:
- fp = builtins.open(filename, "rb")
-
- # read header fields
- header = fp.read(32+24+32+12)
- size = i32(header, 32), i32(header, 36)
- offset = i32(header, 40)
-
- # load pixel data
- fp.seek(offset)
-
- im = Image.frombytes("P", size, fp.read(size[0] * size[1]))
- im.putpalette(quake2palette)
-
- im.format = "WAL"
- im.format_description = "Quake2 Texture"
-
- # strings are null-terminated
- im.info["name"] = header[:32].split(b"\0", 1)[0]
- next_name = header[56:56+32].split(b"\0", 1)[0]
- if next_name:
- im.info["next_name"] = next_name
-
- return im
+ with builtins.open(filename, "rb") as fp:
+ return imopen(fp)
quake2palette = (
diff --git a/server/www/packages/packages-windows/x86/PIL/WebPImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/WebPImagePlugin.py
index 6837b53..39a8f2e 100644
--- a/server/www/packages/packages-windows/x86/PIL/WebPImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/WebPImagePlugin.py
@@ -1,10 +1,13 @@
-from PIL import Image
-from PIL import ImageFile
+from . import Image, ImageFile, _webp
from io import BytesIO
-from PIL import _webp
_VALID_WEBP_MODES = {
+ "RGBX": True,
+ "RGBA": True,
+ }
+
+_VALID_WEBP_LEGACY_MODES = {
"RGB": True,
"RGBA": True,
}
@@ -30,32 +33,263 @@ class WebPImageFile(ImageFile.ImageFile):
format_description = "WebP image"
def _open(self):
- data, width, height, self.mode, icc_profile, exif = \
- _webp.WebPDecode(self.fp.read())
+ if not _webp.HAVE_WEBPANIM:
+ # Legacy mode
+ data, width, height, self.mode, icc_profile, exif = \
+ _webp.WebPDecode(self.fp.read())
+ if icc_profile:
+ self.info["icc_profile"] = icc_profile
+ if exif:
+ self.info["exif"] = exif
+ self.size = width, height
+ self.fp = BytesIO(data)
+ self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
+ self._n_frames = 1
+ return
+ # Use the newer AnimDecoder API to parse the (possibly) animated file,
+ # and access muxed chunks like ICC/EXIF/XMP.
+ self._decoder = _webp.WebPAnimDecoder(self.fp.read())
+
+ # Get info from decoder
+ width, height, loop_count, bgcolor, frame_count, mode = \
+ self._decoder.get_info()
+ self.size = width, height
+ self.info["loop"] = loop_count
+ bg_a, bg_r, bg_g, bg_b = \
+ (bgcolor >> 24) & 0xFF, \
+ (bgcolor >> 16) & 0xFF, \
+ (bgcolor >> 8) & 0xFF, \
+ bgcolor & 0xFF
+ self.info["background"] = (bg_r, bg_g, bg_b, bg_a)
+ self._n_frames = frame_count
+ self.mode = mode
+ self.tile = []
+
+ # Attempt to read ICC / EXIF / XMP chunks from file
+ icc_profile = self._decoder.get_chunk("ICCP")
+ exif = self._decoder.get_chunk("EXIF")
+ xmp = self._decoder.get_chunk("XMP ")
if icc_profile:
self.info["icc_profile"] = icc_profile
if exif:
self.info["exif"] = exif
+ if xmp:
+ self.info["xmp"] = xmp
- self.size = width, height
- self.fp = BytesIO(data)
- self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
+ # Initialize seek state
+ self._reset(reset=False)
+ self.seek(0)
def _getexif(self):
- from PIL.JpegImagePlugin import _getexif
+ from .JpegImagePlugin import _getexif
return _getexif(self)
+ @property
+ def n_frames(self):
+ return self._n_frames
+
+ @property
+ def is_animated(self):
+ return self._n_frames > 1
+
+ def seek(self, frame):
+ if not _webp.HAVE_WEBPANIM:
+ return super(WebPImageFile, self).seek(frame)
+
+ # Perform some simple checks first
+ if frame >= self._n_frames:
+ raise EOFError("attempted to seek beyond end of sequence")
+ if frame < 0:
+ raise EOFError("negative frame index is not valid")
+
+ # Set logical frame to requested position
+ self.__logical_frame = frame
+
+ def _reset(self, reset=True):
+ if reset:
+ self._decoder.reset()
+ self.__physical_frame = 0
+ self.__loaded = -1
+ self.__timestamp = 0
+
+ def _get_next(self):
+ # Get next frame
+ ret = self._decoder.get_next()
+ self.__physical_frame += 1
+
+ # Check if an error occurred
+ if ret is None:
+ self._reset() # Reset just to be safe
+ self.seek(0)
+ raise EOFError("failed to decode next frame in WebP file")
+
+ # Compute duration
+ data, timestamp = ret
+ duration = timestamp - self.__timestamp
+ self.__timestamp = timestamp
+
+ # libwebp gives frame end, adjust to start of frame
+ timestamp -= duration
+ return data, timestamp, duration
+
+ def _seek(self, frame):
+ if self.__physical_frame == frame:
+ return # Nothing to do
+ if frame < self.__physical_frame:
+ self._reset() # Rewind to beginning
+ while self.__physical_frame < frame:
+ self._get_next() # Advance to the requested frame
+
+ def load(self):
+ if _webp.HAVE_WEBPANIM:
+ if self.__loaded != self.__logical_frame:
+ self._seek(self.__logical_frame)
+
+ # We need to load the image data for this frame
+ data, timestamp, duration = self._get_next()
+ self.info["timestamp"] = timestamp
+ self.info["duration"] = duration
+ self.__loaded = self.__logical_frame
+
+ # Set tile
+ self.fp = BytesIO(data)
+ self.tile = [("raw", (0, 0) + self.size, 0, self.mode)]
+
+ return super(WebPImageFile, self).load()
+
+ def tell(self):
+ if not _webp.HAVE_WEBPANIM:
+ return super(WebPImageFile, self).tell()
+
+ return self.__logical_frame
+
+
+def _save_all(im, fp, filename):
+ encoderinfo = im.encoderinfo.copy()
+ append_images = list(encoderinfo.get("append_images", []))
+
+ # If total frame count is 1, then save using the legacy API, which
+ # will preserve non-alpha modes
+ total = 0
+ for ims in [im]+append_images:
+ total += 1 if not hasattr(ims, "n_frames") else ims.n_frames
+ if total == 1:
+ _save(im, fp, filename)
+ return
+
+ background = encoderinfo.get("background", (0, 0, 0, 0))
+ duration = im.encoderinfo.get("duration", 0)
+ loop = im.encoderinfo.get("loop", 0)
+ minimize_size = im.encoderinfo.get("minimize_size", False)
+ kmin = im.encoderinfo.get("kmin", None)
+ kmax = im.encoderinfo.get("kmax", None)
+ allow_mixed = im.encoderinfo.get("allow_mixed", False)
+ verbose = False
+ lossless = im.encoderinfo.get("lossless", False)
+ quality = im.encoderinfo.get("quality", 80)
+ method = im.encoderinfo.get("method", 0)
+ icc_profile = im.encoderinfo.get("icc_profile", "")
+ exif = im.encoderinfo.get("exif", "")
+ xmp = im.encoderinfo.get("xmp", "")
+ if allow_mixed:
+ lossless = False
+
+ # Sensible keyframe defaults are from gif2webp.c script
+ if kmin is None:
+ kmin = 9 if lossless else 3
+ if kmax is None:
+ kmax = 17 if lossless else 5
+
+ # Validate background color
+ if (not isinstance(background, (list, tuple)) or len(background) != 4 or
+ not all(v >= 0 and v < 256 for v in background)):
+ raise IOError("Background color is not an RGBA tuple clamped "
+ "to (0-255): %s" % str(background))
+
+ # Convert to packed uint
+ bg_r, bg_g, bg_b, bg_a = background
+ background = (bg_a << 24) | (bg_r << 16) | (bg_g << 8) | (bg_b << 0)
+
+ # Setup the WebP animation encoder
+ enc = _webp.WebPAnimEncoder(
+ im.size[0], im.size[1],
+ background,
+ loop,
+ minimize_size,
+ kmin, kmax,
+ allow_mixed,
+ verbose
+ )
+
+ # Add each frame
+ frame_idx = 0
+ timestamp = 0
+ cur_idx = im.tell()
+ try:
+ for ims in [im]+append_images:
+ # Get # of frames in this image
+ if not hasattr(ims, "n_frames"):
+ nfr = 1
+ else:
+ nfr = ims.n_frames
+
+ for idx in range(nfr):
+ ims.seek(idx)
+ ims.load()
+
+ # Make sure image mode is supported
+ frame = ims
+ if ims.mode not in _VALID_WEBP_MODES:
+ alpha = ims.mode == 'P' and 'A' in ims.im.getpalettemode()
+ frame = ims.convert('RGBA' if alpha else 'RGBX')
+
+ # Append the frame to the animation encoder
+ enc.add(
+ frame.tobytes(),
+ timestamp,
+ frame.size[0], frame.size[1],
+ frame.mode,
+ lossless,
+ quality,
+ method
+ )
+
+ # Update timestamp and frame index
+ if isinstance(duration, (list, tuple)):
+ timestamp += duration[frame_idx]
+ else:
+ timestamp += duration
+ frame_idx += 1
+
+ finally:
+ im.seek(cur_idx)
+
+ # Force encoder to flush frames
+ enc.add(
+ None,
+ timestamp,
+ 0, 0, "", lossless, quality, 0
+ )
+
+ # Get the final output from the encoder
+ data = enc.assemble(icc_profile, exif, xmp)
+ if data is None:
+ raise IOError("cannot write file as WebP (encoder returned None)")
+
+ fp.write(data)
+
def _save(im, fp, filename):
- image_mode = im.mode
- if im.mode not in _VALID_WEBP_MODES:
- raise IOError("cannot write mode %s as WEBP" % image_mode)
-
lossless = im.encoderinfo.get("lossless", False)
quality = im.encoderinfo.get("quality", 80)
icc_profile = im.encoderinfo.get("icc_profile", "")
exif = im.encoderinfo.get("exif", "")
+ xmp = im.encoderinfo.get("xmp", "")
+
+ if im.mode not in _VALID_WEBP_LEGACY_MODES:
+ alpha = im.mode == 'P' and 'A' in im.im.getpalettemode()
+ im = im.convert('RGBA' if alpha else 'RGB')
data = _webp.WebPEncode(
im.tobytes(),
@@ -65,16 +299,18 @@ def _save(im, fp, filename):
float(quality),
im.mode,
icc_profile,
- exif
+ exif,
+ xmp
)
if data is None:
- raise IOError("cannot write file as WEBP (encoder returned None)")
+ raise IOError("cannot write file as WebP (encoder returned None)")
fp.write(data)
Image.register_open(WebPImageFile.format, WebPImageFile, _accept)
Image.register_save(WebPImageFile.format, _save)
-
+if _webp.HAVE_WEBPANIM:
+ Image.register_save_all(WebPImageFile.format, _save_all)
Image.register_extension(WebPImageFile.format, ".webp")
Image.register_mime(WebPImageFile.format, "image/webp")
diff --git a/server/www/packages/packages-windows/x86/PIL/WmfImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/WmfImagePlugin.py
index 3163210..2135844 100644
--- a/server/www/packages/packages-windows/x86/PIL/WmfImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/WmfImagePlugin.py
@@ -14,26 +14,36 @@
#
# See the README file for information on usage and redistribution.
#
+# WMF/EMF reference documentation:
+# https://winprotocoldoc.blob.core.windows.net/productionwindowsarchives/MS-WMF/[MS-WMF].pdf
+# http://wvware.sourceforge.net/caolan/index.html
+# http://wvware.sourceforge.net/caolan/ora-wmf.html
+
+from __future__ import print_function
+
+from . import Image, ImageFile
+from ._binary import i16le as word, si16le as short, i32le as dword, si32le as _long
+from ._util import py3
-from PIL import Image, ImageFile, _binary
__version__ = "0.2"
_handler = None
-if str != bytes:
+if py3:
long = int
-##
-# Install application-specific WMF image handler.
-#
-# @param handler Handler object.
-
def register_handler(handler):
+ """
+ Install application-specific WMF image handler.
+
+ :param handler: Handler object.
+ """
global _handler
_handler = handler
+
if hasattr(Image.core, "drawwmf"):
# install default handler (windows only)
@@ -53,24 +63,11 @@ if hasattr(Image.core, "drawwmf"):
register_handler(WmfHandler())
-# --------------------------------------------------------------------
-
-word = _binary.i16le
-
-
-def short(c, o=0):
- v = word(c, o)
- if v >= 32768:
- v -= 65536
- return v
-
-dword = _binary.i32le
-
-
#
# --------------------------------------------------------------------
# Read WMF file
+
def _accept(prefix):
return (
prefix[:6] == b"\xd7\xcd\xc6\x9a\x00\x00" or
@@ -111,7 +108,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
self.info["dpi"] = 72
- # print self.mode, self.size, self.info
+ # print(self.mode, self.size, self.info)
# sanity check (standard metafile header)
if s[22:26] != b"\x01\x00\t\x00":
@@ -121,13 +118,13 @@ class WmfStubImageFile(ImageFile.StubImageFile):
# enhanced metafile
# get bounding box
- x0 = dword(s, 8)
- y0 = dword(s, 12)
- x1 = dword(s, 16)
- y1 = dword(s, 20)
+ x0 = _long(s, 8)
+ y0 = _long(s, 12)
+ x1 = _long(s, 16)
+ y1 = _long(s, 20)
# get frame (in 0.01 millimeter units)
- frame = dword(s, 24), dword(s, 28), dword(s, 32), dword(s, 36)
+ frame = _long(s, 24), _long(s, 28), _long(s, 32), _long(s, 36)
# normalize size to 72 dots per inch
size = x1 - x0, y1 - y0
@@ -158,7 +155,7 @@ class WmfStubImageFile(ImageFile.StubImageFile):
def _save(im, fp, filename):
- if _handler is None or not hasattr("_handler", "save"):
+ if _handler is None or not hasattr(_handler, "save"):
raise IOError("WMF save handler not installed")
_handler.save(im, fp, filename)
@@ -166,8 +163,8 @@ def _save(im, fp, filename):
# --------------------------------------------------------------------
# Registry stuff
+
Image.register_open(WmfStubImageFile.format, WmfStubImageFile, _accept)
Image.register_save(WmfStubImageFile.format, _save)
-Image.register_extension(WmfStubImageFile.format, ".wmf")
-Image.register_extension(WmfStubImageFile.format, ".emf")
+Image.register_extensions(WmfStubImageFile.format, [".wmf", ".emf"])
diff --git a/server/www/packages/packages-windows/x86/PIL/XVThumbImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/XVThumbImagePlugin.py
index 9fe9ca1..a7d39ed 100644
--- a/server/www/packages/packages-windows/x86/PIL/XVThumbImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/XVThumbImagePlugin.py
@@ -17,12 +17,11 @@
# FIXME: make save work (this requires quantization support)
#
-from PIL import Image, ImageFile, ImagePalette, _binary
+from . import Image, ImageFile, ImagePalette
+from ._binary import i8, o8
__version__ = "0.1"
-o8 = _binary.o8
-
_MAGIC = b"P7 332"
# standard color palette for thumbnails (RGB332)
@@ -32,6 +31,7 @@ for r in range(8):
for b in range(4):
PALETTE = PALETTE + (o8((r*255)//7)+o8((g*255)//7)+o8((b*255)//3))
+
def _accept(prefix):
return prefix[:6] == _MAGIC
@@ -47,7 +47,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
def _open(self):
# check magic
- if self.fp.read(6) != _MAGIC:
+ if not _accept(self.fp.read(6)):
raise SyntaxError("not an XV thumbnail file")
# Skip to beginning of next line
@@ -58,14 +58,14 @@ class XVThumbImageFile(ImageFile.ImageFile):
s = self.fp.readline()
if not s:
raise SyntaxError("Unexpected EOF reading XV thumbnail file")
- if s[0] != b'#':
+ if i8(s[0]) != 35: # ie. when not a comment: '#'
break
# parse header line (already read)
s = s.strip().split()
self.mode = "P"
- self.size = int(s[0:1]), int(s[1:2])
+ self.size = int(s[0]), int(s[1])
self.palette = ImagePalette.raw("RGB", PALETTE)
@@ -74,6 +74,7 @@ class XVThumbImageFile(ImageFile.ImageFile):
self.fp.tell(), (self.mode, 0, 1)
)]
+
# --------------------------------------------------------------------
Image.register_open(XVThumbImageFile.format, XVThumbImageFile, _accept)
diff --git a/server/www/packages/packages-windows/x86/PIL/XbmImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/XbmImagePlugin.py
index bca8828..b43fbef 100644
--- a/server/www/packages/packages-windows/x86/PIL/XbmImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/XbmImagePlugin.py
@@ -20,13 +20,13 @@
#
import re
-from PIL import Image, ImageFile
+from . import Image, ImageFile
__version__ = "0.6"
# XBM header
xbm_head = re.compile(
- b"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+"
+ br"\s*#define[ \t]+.*_width[ \t]+(?P[0-9]+)[\r\n]+"
b"#define[ \t]+.*_height[ \t]+(?P[0-9]+)[\r\n]+"
b"(?P"
b"#define[ \t]+[^_]*_x_hot[ \t]+(?P[0-9]+)[\r\n]+"
diff --git a/server/www/packages/packages-windows/x86/PIL/XpmImagePlugin.py b/server/www/packages/packages-windows/x86/PIL/XpmImagePlugin.py
index 556adb8..a5cca0e 100644
--- a/server/www/packages/packages-windows/x86/PIL/XpmImagePlugin.py
+++ b/server/www/packages/packages-windows/x86/PIL/XpmImagePlugin.py
@@ -16,8 +16,8 @@
import re
-from PIL import Image, ImageFile, ImagePalette
-from PIL._binary import i8, o8
+from . import Image, ImageFile, ImagePalette
+from ._binary import i8, o8
__version__ = "0.2"
@@ -116,13 +116,12 @@ class XpmImageFile(ImageFile.ImageFile):
for i in range(ysize):
s[i] = self.fp.readline()[1:xsize+1].ljust(xsize)
- self.fp = None
-
return b"".join(s)
#
# Registry
+
Image.register_open(XpmImageFile.format, XpmImageFile, _accept)
Image.register_extension(XpmImageFile.format, ".xpm")
diff --git a/server/www/packages/packages-windows/x86/PIL/__init__.py b/server/www/packages/packages-windows/x86/PIL/__init__.py
index e5dcf43..a07280e 100644
--- a/server/www/packages/packages-windows/x86/PIL/__init__.py
+++ b/server/www/packages/packages-windows/x86/PIL/__init__.py
@@ -1,20 +1,34 @@
-#
-# The Python Imaging Library.
-# $Id$
-#
-# package placeholder
-#
-# Copyright (c) 1999 by Secret Labs AB.
-#
-# See the README file for information on usage and redistribution.
-#
+"""Pillow {} (Fork of the Python Imaging Library)
-# ;-)
+Pillow is the friendly PIL fork by Alex Clark and Contributors.
+ https://github.com/python-pillow/Pillow/
-VERSION = '1.1.7' # PIL version
-PILLOW_VERSION = '3.3.0' # Pillow
+Pillow is forked from PIL 1.1.7.
-_plugins = ['BmpImagePlugin',
+PIL is the Python Imaging Library by Fredrik Lundh and Contributors.
+Copyright (c) 1999 by Secret Labs AB.
+
+Use PIL.__version__ for this Pillow version.
+PIL.VERSION is the old PIL version and will be removed in the future.
+
+;-)
+"""
+
+from . import _version
+
+# VERSION is deprecated and will be removed in Pillow 6.0.0.
+# PILLOW_VERSION is deprecated and will be removed after that.
+# Use __version__ instead.
+VERSION = '1.1.7' # PIL Version
+PILLOW_VERSION = __version__ = _version.__version__
+
+del _version
+
+__doc__ = __doc__.format(__version__) # include version in docstring
+
+
+_plugins = ['BlpImagePlugin',
+ 'BmpImagePlugin',
'BufrStubImagePlugin',
'CurImagePlugin',
'DcxImagePlugin',
diff --git a/server/www/packages/packages-windows/x86/PIL/_binary.py b/server/www/packages/packages-windows/x86/PIL/_binary.py
index 2f5e8ff..767c13b 100644
--- a/server/www/packages/packages-windows/x86/PIL/_binary.py
+++ b/server/www/packages/packages-windows/x86/PIL/_binary.py
@@ -11,50 +11,70 @@
# See the README file for information on usage and redistribution.
#
-from struct import unpack, pack
+from struct import unpack_from, pack
+from ._util import py3
-if bytes is str:
- def i8(c):
- return ord(c)
-
- def o8(i):
- return chr(i & 255)
-else:
+if py3:
def i8(c):
return c if c.__class__ is int else c[0]
def o8(i):
return bytes((i & 255,))
+else:
+ def i8(c):
+ return ord(c)
+
+ def o8(i):
+ return chr(i & 255)
# Input, le = little endian, be = big endian
-# TODO: replace with more readable struct.unpack equivalent
def i16le(c, o=0):
"""
- Converts a 2-bytes (16 bits) string to an integer.
+ Converts a 2-bytes (16 bits) string to an unsigned integer.
c: string containing bytes to convert
o: offset of bytes to convert in string
"""
- return unpack("H", c[o:o+2])[0]
+ return unpack_from(">H", c, o)[0]
def i32be(c, o=0):
- return unpack(">I", c[o:o+4])[0]
+ return unpack_from(">I", c, o)[0]
# Output, le = little endian, be = big endian
@@ -72,5 +92,3 @@ def o16be(i):
def o32be(i):
return pack(">I", i)
-
-# End of file
diff --git a/server/www/packages/packages-windows/x86/PIL/_imaging.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imaging.cp37-win32.pyd
new file mode 100644
index 0000000..e4d5937
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imaging.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imaging.pyd b/server/www/packages/packages-windows/x86/PIL/_imaging.pyd
deleted file mode 100644
index cecd80d..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imaging.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingcms.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingcms.cp37-win32.pyd
new file mode 100644
index 0000000..2678c32
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imagingcms.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingcms.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingcms.pyd
deleted file mode 100644
index 6ed1456..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imagingcms.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingft.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingft.cp37-win32.pyd
new file mode 100644
index 0000000..41634bb
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imagingft.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingft.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingft.pyd
deleted file mode 100644
index 3617f51..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imagingft.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingmath.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingmath.cp37-win32.pyd
new file mode 100644
index 0000000..8e031db
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imagingmath.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingmath.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingmath.pyd
deleted file mode 100644
index 845a52f..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imagingmath.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingmorph.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingmorph.cp37-win32.pyd
new file mode 100644
index 0000000..faa96ef
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imagingmorph.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingmorph.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingmorph.pyd
deleted file mode 100644
index f15966b..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imagingmorph.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingtk.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingtk.cp37-win32.pyd
new file mode 100644
index 0000000..fed76bf
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_imagingtk.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_imagingtk.pyd b/server/www/packages/packages-windows/x86/PIL/_imagingtk.pyd
deleted file mode 100644
index 7f02fe0..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_imagingtk.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_tkinter_finder.py b/server/www/packages/packages-windows/x86/PIL/_tkinter_finder.py
index df41591..987d962 100644
--- a/server/www/packages/packages-windows/x86/PIL/_tkinter_finder.py
+++ b/server/www/packages/packages-windows/x86/PIL/_tkinter_finder.py
@@ -2,7 +2,7 @@
"""
import sys
-if sys.version_info[0] > 2:
+if sys.version_info.major > 2:
from tkinter import _tkinter as tk
else:
from Tkinter import tkinter as tk
@@ -11,7 +11,7 @@ if hasattr(sys, 'pypy_find_executable'):
# Tested with packages at https://bitbucket.org/pypy/pypy/downloads.
# PyPies 1.6, 2.0 do not have tkinter built in. PyPy3-2.3.1 gives an
# OSError trying to import tkinter. Otherwise:
- try: # PyPy 5.1, 4.0.0, 2.6.1, 2.6.0
+ try: # PyPy 5.1, 4.0.0, 2.6.1, 2.6.0
TKINTER_LIB = tk.tklib_cffi.__file__
except AttributeError:
# PyPy3 2.4, 2.1-beta1; PyPy 2.5.1, 2.5.0, 2.4.0, 2.3, 2.2, 2.1
diff --git a/server/www/packages/packages-windows/x86/PIL/_util.py b/server/www/packages/packages-windows/x86/PIL/_util.py
index 51c6f68..6618c62 100644
--- a/server/www/packages/packages-windows/x86/PIL/_util.py
+++ b/server/www/packages/packages-windows/x86/PIL/_util.py
@@ -1,17 +1,19 @@
-import os
+import os, sys
-if bytes is str:
- def isStringType(t):
- return isinstance(t, basestring)
+py3 = sys.version_info.major >= 3
- def isPath(f):
- return isinstance(f, basestring)
-else:
+if py3:
def isStringType(t):
return isinstance(t, str)
def isPath(f):
return isinstance(f, (bytes, str))
+else:
+ def isStringType(t):
+ return isinstance(t, basestring)
+
+ def isPath(f):
+ return isinstance(f, basestring)
# Checks if an object is a string, and that it points to a directory.
diff --git a/server/www/packages/packages-windows/x86/PIL/_version.py b/server/www/packages/packages-windows/x86/PIL/_version.py
new file mode 100644
index 0000000..b42628d
--- /dev/null
+++ b/server/www/packages/packages-windows/x86/PIL/_version.py
@@ -0,0 +1,2 @@
+# Master version for Pillow
+__version__ = '5.2.0'
diff --git a/server/www/packages/packages-windows/x86/PIL/_webp.cp37-win32.pyd b/server/www/packages/packages-windows/x86/PIL/_webp.cp37-win32.pyd
new file mode 100644
index 0000000..5086304
Binary files /dev/null and b/server/www/packages/packages-windows/x86/PIL/_webp.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/PIL/_webp.pyd b/server/www/packages/packages-windows/x86/PIL/_webp.pyd
deleted file mode 100644
index cbaab26..0000000
Binary files a/server/www/packages/packages-windows/x86/PIL/_webp.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/PIL/features.py b/server/www/packages/packages-windows/x86/PIL/features.py
index fd87f09..9926445 100644
--- a/server/www/packages/packages-windows/x86/PIL/features.py
+++ b/server/www/packages/packages-windows/x86/PIL/features.py
@@ -1,46 +1,30 @@
-from PIL import Image
+from . import Image
modules = {
"pil": "PIL._imaging",
- "tkinter": "PIL._imagingtk",
+ "tkinter": "PIL._tkinter_finder",
"freetype2": "PIL._imagingft",
"littlecms2": "PIL._imagingcms",
"webp": "PIL._webp",
- "transp_webp": ("WEBP", "WebPDecoderBuggyAlpha")
}
def check_module(feature):
- if feature not in modules:
+ if not (feature in modules):
raise ValueError("Unknown module %s" % feature)
module = modules[feature]
- method_to_call = None
- if type(module) is tuple:
- module, method_to_call = module
-
try:
- imported_module = __import__(module)
- except ImportError:
- # If a method is being checked, None means that
- # rather than the method failing, the module required for the method
- # failed to be imported first
- return None if method_to_call else False
-
- if method_to_call:
- method = getattr(imported_module, method_to_call)
- return method() is True
- else:
+ __import__(module)
return True
+ except ImportError:
+ return False
def get_supported_modules():
- supported_modules = []
- for feature in modules:
- if check_module(feature):
- supported_modules.append(feature)
- return supported_modules
+ return [f for f in modules if check_module(f)]
+
codecs = {
"jpg": "jpeg",
@@ -60,8 +44,42 @@ def check_codec(feature):
def get_supported_codecs():
- supported_codecs = []
- for feature in codecs:
- if check_codec(feature):
- supported_codecs.append(feature)
- return supported_codecs
+ return [f for f in codecs if check_codec(f)]
+
+
+features = {
+ "webp_anim": ("PIL._webp", 'HAVE_WEBPANIM'),
+ "webp_mux": ("PIL._webp", 'HAVE_WEBPMUX'),
+ "transp_webp": ("PIL._webp", "HAVE_TRANSPARENCY"),
+ "raqm": ("PIL._imagingft", "HAVE_RAQM")
+}
+
+
+def check_feature(feature):
+ if feature not in features:
+ raise ValueError("Unknown feature %s" % feature)
+
+ module, flag = features[feature]
+
+ try:
+ imported_module = __import__(module, fromlist=['PIL'])
+ return getattr(imported_module, flag)
+ except ImportError:
+ return None
+
+
+def get_supported_features():
+ return [f for f in features if check_feature(f)]
+
+
+def check(feature):
+ return (feature in modules and check_module(feature) or
+ feature in codecs and check_codec(feature) or
+ feature in features and check_feature(feature))
+
+
+def get_supported():
+ ret = get_supported_modules()
+ ret.extend(get_supported_features())
+ ret.extend(get_supported_codecs())
+ return ret
diff --git a/server/www/packages/packages-windows/x86/psutil/__init__.py b/server/www/packages/packages-windows/x86/psutil/__init__.py
index d6a66de..e129965 100644
--- a/server/www/packages/packages-windows/x86/psutil/__init__.py
+++ b/server/www/packages/packages-windows/x86/psutil/__init__.py
@@ -10,7 +10,7 @@ sensors) in Python. Supported platforms:
- Linux
- Windows
- - OSX
+ - macOS
- FreeBSD
- OpenBSD
- NetBSD
@@ -43,19 +43,19 @@ from ._common import deprecated_method
from ._common import memoize
from ._common import memoize_when_activated
from ._common import wrap_numbers as _wrap_numbers
-from ._compat import callable
from ._compat import long
from ._compat import PY3 as _PY3
from ._common import STATUS_DEAD
from ._common import STATUS_DISK_SLEEP
-from ._common import STATUS_IDLE # bsd
+from ._common import STATUS_IDLE
from ._common import STATUS_LOCKED
+from ._common import STATUS_PARKED
from ._common import STATUS_RUNNING
from ._common import STATUS_SLEEPING
from ._common import STATUS_STOPPED
from ._common import STATUS_TRACING_STOP
-from ._common import STATUS_WAITING # bsd
+from ._common import STATUS_WAITING
from ._common import STATUS_WAKING
from ._common import STATUS_ZOMBIE
@@ -79,9 +79,10 @@ from ._common import AIX
from ._common import BSD
from ._common import FREEBSD # NOQA
from ._common import LINUX
+from ._common import MACOS
from ._common import NETBSD # NOQA
from ._common import OPENBSD # NOQA
-from ._common import OSX
+from ._common import OSX # deprecated alias
from ._common import POSIX # NOQA
from ._common import SUNOS
from ._common import WINDOWS
@@ -152,7 +153,7 @@ elif WINDOWS:
from ._psutil_windows import REALTIME_PRIORITY_CLASS # NOQA
from ._pswindows import CONN_DELETE_TCB # NOQA
-elif OSX:
+elif MACOS:
from . import _psosx as _psplatform
elif BSD:
@@ -189,6 +190,7 @@ __all__ = [
"STATUS_RUNNING", "STATUS_IDLE", "STATUS_SLEEPING", "STATUS_DISK_SLEEP",
"STATUS_STOPPED", "STATUS_TRACING_STOP", "STATUS_ZOMBIE", "STATUS_DEAD",
"STATUS_WAKING", "STATUS_LOCKED", "STATUS_WAITING", "STATUS_LOCKED",
+ "STATUS_PARKED",
"CONN_ESTABLISHED", "CONN_SYN_SENT", "CONN_SYN_RECV", "CONN_FIN_WAIT1",
"CONN_FIN_WAIT2", "CONN_TIME_WAIT", "CONN_CLOSE", "CONN_CLOSE_WAIT",
@@ -200,8 +202,8 @@ __all__ = [
"POWER_TIME_UNKNOWN", "POWER_TIME_UNLIMITED",
- "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "OSX", "POSIX", "SUNOS",
- "WINDOWS", "AIX",
+ "BSD", "FREEBSD", "LINUX", "NETBSD", "OPENBSD", "MACOS", "OSX", "POSIX",
+ "SUNOS", "WINDOWS", "AIX",
# classes
"Process", "Popen",
@@ -219,7 +221,7 @@ __all__ = [
]
__all__.extend(_psplatform.__extra__all__)
__author__ = "Giampaolo Rodola'"
-__version__ = "5.4.2"
+__version__ = "5.4.7"
version_info = tuple([int(num) for num in __version__.split('.')])
AF_LINK = _psplatform.AF_LINK
POWER_TIME_UNLIMITED = _common.POWER_TIME_UNLIMITED
@@ -266,13 +268,9 @@ else:
ret = {}
for pid in pids():
try:
- proc = _psplatform.Process(pid)
- ppid = proc.ppid()
- except (NoSuchProcess, AccessDenied):
- # Note: AccessDenied is unlikely to happen.
+ ret[pid] = _psplatform.Process(pid).ppid()
+ except (NoSuchProcess, ZombieProcess):
pass
- else:
- ret[pid] = ppid
return ret
@@ -828,7 +826,7 @@ class Process(object):
"""
return self._proc.cpu_num()
- # Linux, OSX and Windows only
+ # Linux, macOS and Windows only
if hasattr(_psplatform.Process, "environ"):
def environ(self):
@@ -1032,7 +1030,7 @@ class Process(object):
namedtuple representing the accumulated process time, in
seconds.
This is similar to os.times() but per-process.
- On OSX and Windows children_user and children_system are
+ On macOS and Windows children_user and children_system are
always set to 0.
"""
return self._proc.cpu_times()
@@ -1054,7 +1052,7 @@ class Process(object):
def memory_full_info(self):
"""This method returns the same information as memory_info(),
- plus, on some platform (Linux, OSX, Windows), also provides
+ plus, on some platform (Linux, macOS, Windows), also provides
additional metrics (USS, PSS and swap).
The additional metrics provide a better representation of actual
process memory usage.
@@ -1656,6 +1654,27 @@ def _cpu_busy_time(times):
return busy
+def _cpu_times_deltas(t1, t2):
+ assert t1._fields == t2._fields, (t1, t2)
+ field_deltas = []
+ for field in _psplatform.scputimes._fields:
+ field_delta = getattr(t2, field) - getattr(t1, field)
+ # CPU times are always supposed to increase over time
+ # or at least remain the same and that's because time
+ # cannot go backwards.
+ # Surprisingly sometimes this might not be the case (at
+ # least on Windows and Linux), see:
+ # https://github.com/giampaolo/psutil/issues/392
+ # https://github.com/giampaolo/psutil/issues/645
+ # https://github.com/giampaolo/psutil/issues/1210
+ # Trim negative deltas to zero to ignore decreasing fields.
+ # top does the same. Reference:
+ # https://gitlab.com/procps-ng/procps/blob/v3.3.12/top/top.c#L5063
+ field_delta = max(0, field_delta)
+ field_deltas.append(field_delta)
+ return _psplatform.scputimes(*field_deltas)
+
+
def cpu_percent(interval=None, percpu=False):
"""Return a float representing the current system-wide CPU
utilization as a percentage.
@@ -1698,18 +1717,11 @@ def cpu_percent(interval=None, percpu=False):
raise ValueError("interval is not positive (got %r)" % interval)
def calculate(t1, t2):
- t1_all = _cpu_tot_time(t1)
- t1_busy = _cpu_busy_time(t1)
+ times_delta = _cpu_times_deltas(t1, t2)
- t2_all = _cpu_tot_time(t2)
- t2_busy = _cpu_busy_time(t2)
+ all_delta = _cpu_tot_time(times_delta)
+ busy_delta = _cpu_busy_time(times_delta)
- # this usually indicates a float precision issue
- if t2_busy <= t1_busy:
- return 0.0
-
- busy_delta = t2_busy - t1_busy
- all_delta = t2_all - t1_all
try:
busy_perc = (busy_delta / all_delta) * 100
except ZeroDivisionError:
@@ -1778,28 +1790,18 @@ def cpu_times_percent(interval=None, percpu=False):
def calculate(t1, t2):
nums = []
- all_delta = _cpu_tot_time(t2) - _cpu_tot_time(t1)
- for field in t1._fields:
- field_delta = getattr(t2, field) - getattr(t1, field)
- try:
- field_perc = (100 * field_delta) / all_delta
- except ZeroDivisionError:
- field_perc = 0.0
+ times_delta = _cpu_times_deltas(t1, t2)
+ all_delta = _cpu_tot_time(times_delta)
+ # "scale" is the value to multiply each delta with to get percentages.
+ # We use "max" to avoid division by zero (if all_delta is 0, then all
+ # fields are 0 so percentages will be 0 too. all_delta cannot be a
+ # fraction because cpu times are integers)
+ scale = 100.0 / max(1, all_delta)
+ for field_delta in times_delta:
+ field_perc = field_delta * scale
field_perc = round(field_perc, 1)
- # CPU times are always supposed to increase over time
- # or at least remain the same and that's because time
- # cannot go backwards.
- # Surprisingly sometimes this might not be the case (at
- # least on Windows and Linux), see:
- # https://github.com/giampaolo/psutil/issues/392
- # https://github.com/giampaolo/psutil/issues/645
- # I really don't know what to do about that except
- # forcing the value to 0 or 100.
- if field_perc > 100.0:
- field_perc = 100.0
- # `<=` because `-0.0 == 0.0` evaluates to True
- elif field_perc <= 0.0:
- field_perc = 0.0
+ # make sure we don't return negative values or values over 100%
+ field_perc = min(max(0.0, field_perc), 100.0)
nums.append(field_perc)
return _psplatform.scputimes(*nums)
@@ -1899,9 +1901,9 @@ def virtual_memory():
- used:
memory used, calculated differently depending on the platform and
designed for informational purposes only:
- OSX: active + inactive + wired
+ macOS: active + inactive + wired
BSD: active + wired + cached
- LINUX: total - free
+ Linux: total - free
- free:
memory not being used at all (zeroed) that is readily available;
@@ -1919,10 +1921,10 @@ def virtual_memory():
- buffers (BSD, Linux):
cache for things like file system metadata.
- - cached (BSD, OSX):
+ - cached (BSD, macOS):
cache for various things.
- - wired (OSX, BSD):
+ - wired (macOS, BSD):
memory that is marked to always stay in RAM. It is never moved to disk.
- shared (BSD):
@@ -2011,7 +2013,8 @@ def disk_io_counters(perdisk=False, nowrap=True):
On recent Windows versions 'diskperf -y' command may need to be
executed first otherwise this function won't find any disk.
"""
- rawdict = _psplatform.disk_io_counters()
+ kwargs = dict(perdisk=perdisk) if LINUX else {}
+ rawdict = _psplatform.disk_io_counters(**kwargs)
if not rawdict:
return {} if perdisk else None
if nowrap:
@@ -2047,7 +2050,7 @@ def net_io_counters(pernic=False, nowrap=True):
- errout: total number of errors while sending
- dropin: total number of incoming packets which were dropped
- dropout: total number of outgoing packets which were dropped
- (always 0 on OSX and BSD)
+ (always 0 on macOS and BSD)
If *pernic* is True return the same information for every
network interface installed on the system as a dictionary
@@ -2103,7 +2106,7 @@ def net_connections(kind='inet'):
| all | the sum of all the possible families and protocols |
+------------+----------------------------------------------------+
- On OSX this function requires root privileges.
+ On macOS this function requires root privileges.
"""
return _psplatform.net_connections(kind)
@@ -2152,7 +2155,7 @@ def net_if_addrs():
separator = ":" if POSIX else "-"
while addr.count(separator) < 5:
addr += "%s00" % separator
- ret[name].append(_common.snic(fam, addr, mask, broadcast, ptp))
+ ret[name].append(_common.snicaddr(fam, addr, mask, broadcast, ptp))
return dict(ret)
@@ -2176,7 +2179,7 @@ def net_if_stats():
# =====================================================================
-# Linux
+# Linux, macOS
if hasattr(_psplatform, "sensors_temperatures"):
def sensors_temperatures(fahrenheit=False):
@@ -2214,7 +2217,7 @@ if hasattr(_psplatform, "sensors_temperatures"):
__all__.append("sensors_temperatures")
-# Linux
+# Linux, macOS
if hasattr(_psplatform, "sensors_fans"):
def sensors_fans():
@@ -2227,7 +2230,7 @@ if hasattr(_psplatform, "sensors_fans"):
__all__.append("sensors_fans")
-# Linux, Windows, FreeBSD, OSX
+# Linux, Windows, FreeBSD, macOS
if hasattr(_psplatform, "sensors_battery"):
def sensors_battery():
@@ -2270,13 +2273,6 @@ def users():
return _psplatform.users()
-def set_procfs_path(path):
- """Set an alternative path for /proc filesystem on Linux, Solaris
- and AIX. This superseds PROCFS_PATH variable which is deprecated.
- """
- _psplatform.PROCFS_PATH = path
-
-
# =====================================================================
# --- Windows services
# =====================================================================
diff --git a/server/www/packages/packages-windows/x86/psutil/_common.py b/server/www/packages/packages-windows/x86/psutil/_common.py
index 870971e..2cc3939 100644
--- a/server/www/packages/packages-windows/x86/psutil/_common.py
+++ b/server/www/packages/packages-windows/x86/psutil/_common.py
@@ -42,8 +42,8 @@ PY3 = sys.version_info[0] == 3
__all__ = [
# constants
- 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'OSX', 'POSIX', 'SUNOS',
- 'WINDOWS',
+ 'FREEBSD', 'BSD', 'LINUX', 'NETBSD', 'OPENBSD', 'MACOS', 'OSX', 'POSIX',
+ 'SUNOS', 'WINDOWS',
'ENCODING', 'ENCODING_ERRS', 'AF_INET6',
# connection constants
'CONN_CLOSE', 'CONN_CLOSE_WAIT', 'CONN_CLOSING', 'CONN_ESTABLISHED',
@@ -55,11 +55,11 @@ __all__ = [
'STATUS_DEAD', 'STATUS_DISK_SLEEP', 'STATUS_IDLE', 'STATUS_LOCKED',
'STATUS_RUNNING', 'STATUS_SLEEPING', 'STATUS_STOPPED', 'STATUS_SUSPENDED',
'STATUS_TRACING_STOP', 'STATUS_WAITING', 'STATUS_WAKE_KILL',
- 'STATUS_WAKING', 'STATUS_ZOMBIE',
+ 'STATUS_WAKING', 'STATUS_ZOMBIE', 'STATUS_PARKED',
# named tuples
'pconn', 'pcputimes', 'pctxsw', 'pgids', 'pio', 'pionice', 'popenfile',
'pthread', 'puids', 'sconn', 'scpustats', 'sdiskio', 'sdiskpart',
- 'sdiskusage', 'snetio', 'snic', 'snicstats', 'sswap', 'suser',
+ 'sdiskusage', 'snetio', 'snicaddr', 'snicstats', 'sswap', 'suser',
# utility functions
'conn_tmap', 'deprecated_method', 'isfile_strict', 'memoize',
'parse_environ_block', 'path_exists_strict', 'usage_percent',
@@ -75,12 +75,13 @@ __all__ = [
POSIX = os.name == "posix"
WINDOWS = os.name == "nt"
LINUX = sys.platform.startswith("linux")
-OSX = sys.platform.startswith("darwin")
+MACOS = sys.platform.startswith("darwin")
+OSX = MACOS # deprecated alias
FREEBSD = sys.platform.startswith("freebsd")
OPENBSD = sys.platform.startswith("openbsd")
NETBSD = sys.platform.startswith("netbsd")
BSD = FREEBSD or OPENBSD or NETBSD
-SUNOS = sys.platform.startswith("sunos") or sys.platform.startswith("solaris")
+SUNOS = sys.platform.startswith(("sunos", "solaris"))
AIX = sys.platform.startswith("aix")
@@ -99,10 +100,11 @@ STATUS_ZOMBIE = "zombie"
STATUS_DEAD = "dead"
STATUS_WAKE_KILL = "wake-kill"
STATUS_WAKING = "waking"
-STATUS_IDLE = "idle" # FreeBSD, OSX
+STATUS_IDLE = "idle" # Linux, macOS, FreeBSD
STATUS_LOCKED = "locked" # FreeBSD
STATUS_WAITING = "waiting" # FreeBSD
STATUS_SUSPENDED = "suspended" # NetBSD
+STATUS_PARKED = "parked" # Linux
# Process.connections() and psutil.net_connections()
CONN_ESTABLISHED = "ESTABLISHED"
@@ -182,7 +184,8 @@ suser = namedtuple('suser', ['name', 'terminal', 'host', 'started', 'pid'])
sconn = namedtuple('sconn', ['fd', 'family', 'type', 'laddr', 'raddr',
'status', 'pid'])
# psutil.net_if_addrs()
-snic = namedtuple('snic', ['family', 'address', 'netmask', 'broadcast', 'ptp'])
+snicaddr = namedtuple('snicaddr',
+ ['family', 'address', 'netmask', 'broadcast', 'ptp'])
# psutil.net_if_stats()
snicstats = namedtuple('snicstats', ['isup', 'duplex', 'speed', 'mtu'])
# psutil.cpu_stats()
@@ -195,7 +198,7 @@ shwtemp = namedtuple(
'shwtemp', ['label', 'current', 'high', 'critical'])
# psutil.sensors_battery()
sbattery = namedtuple('sbattery', ['percent', 'secsleft', 'power_plugged'])
-# psutil.sensors_battery()
+# psutil.sensors_fans()
sfan = namedtuple('sfan', ['label', 'current'])
# --- for Process methods
@@ -261,14 +264,14 @@ del AF_INET, AF_UNIX, SOCK_STREAM, SOCK_DGRAM
# ===================================================================
-def usage_percent(used, total, _round=None):
+def usage_percent(used, total, round_=None):
"""Calculate percentage usage of 'used' against 'total'."""
try:
ret = (used / total) * 100
except ZeroDivisionError:
ret = 0.0 if isinstance(used, float) or isinstance(total, float) else 0
- if _round is not None:
- return round(ret, _round)
+ if round_ is not None:
+ return round(ret, round_)
else:
return ret
diff --git a/server/www/packages/packages-windows/x86/psutil/_compat.py b/server/www/packages/packages-windows/x86/psutil/_compat.py
index de91638..08aefe4 100644
--- a/server/www/packages/packages-windows/x86/psutil/_compat.py
+++ b/server/www/packages/packages-windows/x86/psutil/_compat.py
@@ -10,7 +10,7 @@ import os
import sys
__all__ = ["PY3", "long", "xrange", "unicode", "basestring", "u", "b",
- "callable", "lru_cache", "which"]
+ "lru_cache", "which"]
PY3 = sys.version_info[0] == 3
@@ -38,14 +38,6 @@ else:
return s
-# removed in 3.0, reintroduced in 3.2
-try:
- callable = callable
-except NameError:
- def callable(obj):
- return any("__call__" in klass.__dict__ for klass in type(obj).__mro__)
-
-
# --- stdlib additions
diff --git a/server/www/packages/packages-windows/x86/psutil/_exceptions.py b/server/www/packages/packages-windows/x86/psutil/_exceptions.py
index c08e6d8..6dbbd28 100644
--- a/server/www/packages/packages-windows/x86/psutil/_exceptions.py
+++ b/server/www/packages/packages-windows/x86/psutil/_exceptions.py
@@ -39,7 +39,7 @@ class NoSuchProcess(Error):
class ZombieProcess(NoSuchProcess):
"""Exception raised when querying a zombie process. This is
- raised on OSX, BSD and Solaris only, and not always: depending
+ raised on macOS, BSD and Solaris only, and not always: depending
on the query the OS may be able to succeed anyway.
On Linux all zombie processes are querable (hence this is never
raised). Windows doesn't have zombie processes.
diff --git a/server/www/packages/packages-windows/x86/psutil/_psaix.py b/server/www/packages/packages-windows/x86/psutil/_psaix.py
index 9abc8d1..7ba212d 100644
--- a/server/www/packages/packages-windows/x86/psutil/_psaix.py
+++ b/server/www/packages/packages-windows/x86/psutil/_psaix.py
@@ -117,7 +117,7 @@ def get_procfs_path():
def virtual_memory():
total, avail, free, pinned, inuse = cext.virtual_mem()
- percent = usage_percent((total - avail), total, _round=1)
+ percent = usage_percent((total - avail), total, round_=1)
return svmem(total, avail, percent, inuse, free)
@@ -125,7 +125,7 @@ def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
total, free, sin, sout = cext.swap_mem()
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent, sin, sout)
@@ -269,7 +269,8 @@ def net_if_stats():
stdout, stderr = [x.decode(sys.stdout.encoding)
for x in (stdout, stderr)]
if p.returncode == 0:
- re_result = re.search("Running: (\d+) Mbps.*?(\w+) Duplex", stdout)
+ re_result = re.search(
+ r"Running: (\d+) Mbps.*?(\w+) Duplex", stdout)
if re_result is not None:
speed = int(re_result.group(1))
duplex = re_result.group(2)
@@ -534,7 +535,7 @@ class Process(object):
for x in (stdout, stderr)]
if "no such process" in stderr.lower():
raise NoSuchProcess(self.pid, self._name)
- procfiles = re.findall("(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout)
+ procfiles = re.findall(r"(\d+): S_IFREG.*\s*.*name:(.*)\n", stdout)
retlist = []
for fd, path in procfiles:
path = path.strip()
diff --git a/server/www/packages/packages-windows/x86/psutil/_psbsd.py b/server/www/packages/packages-windows/x86/psutil/_psbsd.py
index 0553401..7f4bcb6 100644
--- a/server/www/packages/packages-windows/x86/psutil/_psbsd.py
+++ b/server/www/packages/packages-windows/x86/psutil/_psbsd.py
@@ -188,7 +188,7 @@ def virtual_memory():
shared = int(line.split()[1]) * 1024
avail = inactive + cached + free
used = active + wired + cached
- percent = usage_percent((total - avail), total, _round=1)
+ percent = usage_percent((total - avail), total, round_=1)
return svmem(total, avail, percent, used, free,
active, inactive, buffers, cached, shared, wired)
@@ -196,7 +196,7 @@ def virtual_memory():
def swap_memory():
"""System swap memory as (total, used, free, sin, sout) namedtuple."""
total, used, free, sin, sout = cext.swap_mem()
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent, sin, sout)
@@ -345,12 +345,18 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_flags(name)
- duplex, speed = cext_posix.net_if_duplex_speed(name)
- if hasattr(_common, 'NicDuplex'):
- duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ try:
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext_posix.net_if_duplex_speed(name)
+ except OSError as err:
+ # https://github.com/giampaolo/psutil/issues/1279
+ if err.errno != errno.ENODEV:
+ raise
+ else:
+ if hasattr(_common, 'NicDuplex'):
+ duplex = _common.NicDuplex(duplex)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
diff --git a/server/www/packages/packages-windows/x86/psutil/_pslinux.py b/server/www/packages/packages-windows/x86/psutil/_pslinux.py
index b9b4334..df624de 100644
--- a/server/www/packages/packages-windows/x86/psutil/_pslinux.py
+++ b/server/www/packages/packages-windows/x86/psutil/_pslinux.py
@@ -68,7 +68,6 @@ __extra__all__ = [
# =====================================================================
-PROCFS_PATH = None
POWER_SUPPLY_PATH = "/sys/class/power_supply"
HAS_SMAPS = os.path.exists('/proc/%s/smaps' % os.getpid())
HAS_PRLIMIT = hasattr(cext, "linux_prlimit")
@@ -89,7 +88,20 @@ BOOT_TIME = None # set later
# speedup, see: https://github.com/giampaolo/psutil/issues/708
BIGFILE_BUFFERING = -1 if PY3 else 8192
LITTLE_ENDIAN = sys.byteorder == 'little'
-SECTOR_SIZE_FALLBACK = 512
+
+# "man iostat" states that sectors are equivalent with blocks and have
+# a size of 512 bytes. Despite this value can be queried at runtime
+# via /sys/block/{DISK}/queue/hw_sector_size and results may vary
+# between 1k, 2k, or 4k... 512 appears to be a magic constant used
+# throughout Linux source code:
+# * https://stackoverflow.com/a/38136179/376587
+# * https://lists.gt.net/linux/kernel/2241060
+# * https://github.com/giampaolo/psutil/issues/1305
+# * https://github.com/torvalds/linux/blob/
+# 4f671fe2f9523a1ea206f63fe60a7c7b3a56d5c7/include/linux/bio.h#L99
+# * https://lkml.org/lkml/2015/8/17/234
+DISK_SECTOR_SIZE = 512
+
if enum is None:
AF_LINK = socket.AF_PACKET
else:
@@ -112,7 +124,10 @@ else:
globals().update(IOPriority.__members__)
-# taken from /fs/proc/array.c
+# See:
+# https://github.com/torvalds/linux/blame/master/fs/proc/array.c
+# ...and (TASK_* constants):
+# https://github.com/torvalds/linux/blob/master/include/linux/sched.h
PROC_STATUSES = {
"R": _common.STATUS_RUNNING,
"S": _common.STATUS_SLEEPING,
@@ -123,7 +138,9 @@ PROC_STATUSES = {
"X": _common.STATUS_DEAD,
"x": _common.STATUS_DEAD,
"K": _common.STATUS_WAKE_KILL,
- "W": _common.STATUS_WAKING
+ "W": _common.STATUS_WAKING,
+ "I": _common.STATUS_IDLE,
+ "P": _common.STATUS_PARKED,
}
# https://github.com/torvalds/linux/blob/master/include/net/tcp_states.h
@@ -150,7 +167,7 @@ TCP_STATUSES = {
# psutil.virtual_memory()
svmem = namedtuple(
'svmem', ['total', 'available', 'percent', 'used', 'free',
- 'active', 'inactive', 'buffers', 'cached', 'shared'])
+ 'active', 'inactive', 'buffers', 'cached', 'shared', 'slab'])
# psutil.disk_io_counters()
sdiskio = namedtuple(
'sdiskio', ['read_count', 'write_count',
@@ -211,36 +228,8 @@ else:
def get_procfs_path():
- """Return updated PROCFS_PATH constant.
- Return value is cached after 10 calls.
- """
- global PROCFS_PATH
-
- if PROCFS_PATH is not None:
- return PROCFS_PATH
-
- path = sys.modules['psutil'].PROCFS_PATH
- if path != "/proc":
- msg = \
- "you used `psutil.PROCFS_PATH = %s` somewhere in your code; " \
- "that is deprecated and will be ignored in the future; replace " \
- "it with `set_procfs_path(%r)`" % (path, path)
- warnings.warn(msg, category=FutureWarning, stacklevel=2)
- PROCFS_PATH = path
-
- # Cache the value if path remained the same after 10 calls.
- # This means that from now on any change to psutil.PROCFS_PATH
- # will be ignored.
- # This is based on the assumption that it's likely that the user
- # does "psutil.PROCFS_PATH" at import time, not later.
- get_procfs_path.ncalls += 1
- if get_procfs_path.ncalls >= 10:
- PROCFS_PATH = path
-
- return path
-
-
-get_procfs_path.ncalls = 0
+ """Return updated psutil.PROCFS_PATH constant."""
+ return sys.modules['psutil'].PROCFS_PATH
def readlink(path):
@@ -276,17 +265,23 @@ def file_flags_to_mode(flags):
return mode
-def get_sector_size(partition):
- """Return the sector size of a partition.
- Used by disk_io_counters().
+def is_storage_device(name):
+ """Return True if the given name refers to a root device (e.g.
+ "sda", "nvme0n1") as opposed to a logical partition (e.g. "sda1",
+ "nvme0n1p1"). If name is a virtual device (e.g. "loop1", "ram")
+ return True.
"""
- try:
- with open("/sys/block/%s/queue/hw_sector_size" % partition, "rt") as f:
- return int(f.read())
- except (IOError, ValueError):
- # man iostat states that sectors are equivalent with blocks and
- # have a size of 512 bytes since 2.4 kernels.
- return SECTOR_SIZE_FALLBACK
+ # Readapted from iostat source code, see:
+ # https://github.com/sysstat/sysstat/blob/
+ # 97912938cd476645b267280069e83b1c8dc0e1c7/common.c#L208
+ # Some devices may have a slash in their name (e.g. cciss/c0d0...).
+ name = name.replace('/', '!')
+ including_virtual = True
+ if including_virtual:
+ path = "/sys/block/%s" % name
+ else:
+ path = "/sys/block/%s/device" % name
+ return os.access(path, os.F_OK)
@memoize
@@ -323,7 +318,7 @@ def cat(fname, fallback=_DEFAULT, binary=True):
try:
with open_binary(fname) if binary else open_text(fname) as f:
return f.read().strip()
- except IOError:
+ except (IOError, OSError):
if fallback is not _DEFAULT:
return fallback
else:
@@ -470,6 +465,11 @@ def virtual_memory():
inactive = 0
missing_fields.append('inactive')
+ try:
+ slab = mems[b"Slab:"]
+ except KeyError:
+ slab = 0
+
used = total - free - cached - buffers
if used < 0:
# May be symptomatic of running within a LCX container where such
@@ -500,7 +500,7 @@ def virtual_memory():
if avail > total:
avail = free
- percent = usage_percent((total - avail), total, _round=1)
+ percent = usage_percent((total - avail), total, round_=1)
# Warn about missing metrics which are set to 0.
if missing_fields:
@@ -510,7 +510,7 @@ def virtual_memory():
warnings.warn(msg, RuntimeWarning)
return svmem(total, avail, percent, used, free,
- active, inactive, buffers, cached, shared)
+ active, inactive, buffers, cached, shared, slab)
def swap_memory():
@@ -533,7 +533,7 @@ def swap_memory():
free *= unit_multiplier
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
# get pgin/pgouts
try:
f = open_binary("%s/vmstat" % get_procfs_path())
@@ -1026,10 +1026,16 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_flags(name)
- duplex, speed = cext.net_if_duplex_speed(name)
- ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
+ try:
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext.net_if_duplex_speed(name)
+ except OSError as err:
+ # https://github.com/giampaolo/psutil/issues/1279
+ if err.errno != errno.ENODEV:
+ raise
+ else:
+ ret[name] = _common.snicstats(isup, duplex_map[duplex], speed, mtu)
return ret
@@ -1041,35 +1047,11 @@ def net_if_stats():
disk_usage = _psposix.disk_usage
-def disk_io_counters():
+def disk_io_counters(perdisk=False):
"""Return disk I/O statistics for every disk installed on the
system as a dict of raw tuples.
"""
- # determine partitions we want to look for
- def get_partitions():
- partitions = []
- with open_text("%s/partitions" % get_procfs_path()) as f:
- lines = f.readlines()[2:]
- for line in reversed(lines):
- _, _, _, name = line.split()
- if name[-1].isdigit():
- # we're dealing with a partition (e.g. 'sda1'); 'sda' will
- # also be around but we want to omit it
- partitions.append(name)
- else:
- if not partitions or not partitions[-1].startswith(name):
- # we're dealing with a disk entity for which no
- # partitions have been defined (e.g. 'sda' but
- # 'sda1' was not around), see:
- # https://github.com/giampaolo/psutil/issues/338
- partitions.append(name)
- return partitions
-
- retdict = {}
- partitions = get_partitions()
- with open_text("%s/diskstats" % get_procfs_path()) as f:
- lines = f.readlines()
- for line in lines:
+ def read_procfs():
# OK, this is a bit confusing. The format of /proc/diskstats can
# have 3 variations.
# On Linux 2.4 each line has always 15 fields, e.g.:
@@ -1083,33 +1065,77 @@ def disk_io_counters():
# See:
# https://www.kernel.org/doc/Documentation/iostats.txt
# https://www.kernel.org/doc/Documentation/ABI/testing/procfs-diskstats
- fields = line.split()
- fields_len = len(fields)
- if fields_len == 15:
- # Linux 2.4
- name = fields[3]
- reads = int(fields[2])
- (reads_merged, rbytes, rtime, writes, writes_merged,
- wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])
- elif fields_len == 14:
- # Linux 2.6+, line referring to a disk
- name = fields[2]
- (reads, reads_merged, rbytes, rtime, writes, writes_merged,
- wbytes, wtime, _, busy_time, _) = map(int, fields[3:14])
- elif fields_len == 7:
- # Linux 2.6+, line referring to a partition
- name = fields[2]
- reads, rbytes, writes, wbytes = map(int, fields[3:])
- rtime = wtime = reads_merged = writes_merged = busy_time = 0
- else:
- raise ValueError("not sure how to interpret line %r" % line)
+ with open_text("%s/diskstats" % get_procfs_path()) as f:
+ lines = f.readlines()
+ for line in lines:
+ fields = line.split()
+ flen = len(fields)
+ if flen == 15:
+ # Linux 2.4
+ name = fields[3]
+ reads = int(fields[2])
+ (reads_merged, rbytes, rtime, writes, writes_merged,
+ wbytes, wtime, _, busy_time, _) = map(int, fields[4:14])
+ elif flen == 14:
+ # Linux 2.6+, line referring to a disk
+ name = fields[2]
+ (reads, reads_merged, rbytes, rtime, writes, writes_merged,
+ wbytes, wtime, _, busy_time, _) = map(int, fields[3:14])
+ elif flen == 7:
+ # Linux 2.6+, line referring to a partition
+ name = fields[2]
+ reads, rbytes, writes, wbytes = map(int, fields[3:])
+ rtime = wtime = reads_merged = writes_merged = busy_time = 0
+ else:
+ raise ValueError("not sure how to interpret line %r" % line)
+ yield (name, reads, writes, rbytes, wbytes, rtime, wtime,
+ reads_merged, writes_merged, busy_time)
+
+ def read_sysfs():
+ for block in os.listdir('/sys/block'):
+ for root, _, files in os.walk(os.path.join('/sys/block', block)):
+ if 'stat' not in files:
+ continue
+ with open_text(os.path.join(root, 'stat')) as f:
+ fields = f.read().strip().split()
+ name = os.path.basename(root)
+ (reads, reads_merged, rbytes, rtime, writes, writes_merged,
+ wbytes, wtime, _, busy_time, _) = map(int, fields)
+ yield (name, reads, writes, rbytes, wbytes, rtime,
+ wtime, reads_merged, writes_merged, busy_time)
+
+ if os.path.exists('%s/diskstats' % get_procfs_path()):
+ gen = read_procfs()
+ elif os.path.exists('/sys/block'):
+ gen = read_sysfs()
+ else:
+ raise NotImplementedError(
+ "%s/diskstats nor /sys/block filesystem are available on this "
+ "system" % get_procfs_path())
+
+ retdict = {}
+ for entry in gen:
+ (name, reads, writes, rbytes, wbytes, rtime, wtime, reads_merged,
+ writes_merged, busy_time) = entry
+ if not perdisk and not is_storage_device(name):
+ # perdisk=False means we want to calculate totals so we skip
+ # partitions (e.g. 'sda1', 'nvme0n1p1') and only include
+ # base disk devices (e.g. 'sda', 'nvme0n1'). Base disks
+ # include a total of all their partitions + some extra size
+ # of their own:
+ # $ cat /proc/diskstats
+ # 259 0 sda 10485760 ...
+ # 259 1 sda1 5186039 ...
+ # 259 1 sda2 5082039 ...
+ # See:
+ # https://github.com/giampaolo/psutil/pull/1313
+ continue
+
+ rbytes *= DISK_SECTOR_SIZE
+ wbytes *= DISK_SECTOR_SIZE
+ retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime,
+ reads_merged, writes_merged, busy_time)
- if name in partitions:
- ssize = get_sector_size(name)
- rbytes *= ssize
- wbytes *= ssize
- retdict[name] = (reads, writes, rbytes, wbytes, rtime, wtime,
- reads_merged, writes_merged, busy_time)
return retdict
@@ -1169,26 +1195,37 @@ def sensors_temperatures():
for base in basenames:
try:
- current = float(cat(base + '_input')) / 1000.0
- except (IOError, OSError) as err:
+ path = base + '_input'
+ current = float(cat(path)) / 1000.0
+ path = os.path.join(os.path.dirname(base), 'name')
+ unit_name = cat(path, binary=False)
+ except (IOError, OSError, ValueError) as err:
# A lot of things can go wrong here, so let's just skip the
- # whole entry.
+ # whole entry. Sure thing is Linux's /sys/class/hwmon really
+ # is a stinky broken mess.
# https://github.com/giampaolo/psutil/issues/1009
# https://github.com/giampaolo/psutil/issues/1101
# https://github.com/giampaolo/psutil/issues/1129
- warnings.warn("ignoring %r" % err, RuntimeWarning)
+ # https://github.com/giampaolo/psutil/issues/1245
+ # https://github.com/giampaolo/psutil/issues/1323
+ warnings.warn("ignoring %r for file %r" % (err, path),
+ RuntimeWarning)
continue
- unit_name = cat(os.path.join(os.path.dirname(base), 'name'),
- binary=False)
high = cat(base + '_max', fallback=None)
critical = cat(base + '_crit', fallback=None)
label = cat(base + '_label', fallback='', binary=False)
if high is not None:
- high = float(high) / 1000.0
+ try:
+ high = float(high) / 1000.0
+ except ValueError:
+ high = None
if critical is not None:
- critical = float(critical) / 1000.0
+ try:
+ critical = float(critical) / 1000.0
+ except ValueError:
+ critical = None
ret[unit_name].append((label, current, high, critical))
@@ -1246,9 +1283,13 @@ def sensors_battery():
return int(ret) if ret.isdigit() else ret
return None
- root = os.path.join(POWER_SUPPLY_PATH, "BAT0")
- if not os.path.exists(root):
+ bats = [x for x in os.listdir(POWER_SUPPLY_PATH) if x.startswith('BAT')]
+ if not bats:
return None
+ # Get the first available battery. Usually this is "BAT0", except
+ # some rare exceptions:
+ # https://github.com/giampaolo/psutil/issues/1238
+ root = os.path.join(POWER_SUPPLY_PATH, sorted(bats)[0])
# Base metrics.
energy_now = multi_cat(
@@ -1397,9 +1438,8 @@ def ppid_map():
data = f.read()
except EnvironmentError as err:
# Note: we should be able to access /stat for all processes
- # so we won't bump into EPERM, which is good.
- if err.errno not in (errno.ENOENT, errno.ESRCH,
- errno.EPERM, errno.EACCES):
+ # aka it's unlikely we'll bump into EPERM, which is good.
+ if err.errno not in (errno.ENOENT, errno.ESRCH):
raise
else:
rpar = data.rfind(b')')
@@ -1632,9 +1672,10 @@ class Process(object):
@wrap_exceptions
def memory_full_info(
self,
- _private_re=re.compile(br"Private.*:\s+(\d+)"),
- _pss_re=re.compile(br"Pss.*:\s+(\d+)"),
- _swap_re=re.compile(br"Swap.*:\s+(\d+)")):
+ # Gets Private_Clean, Private_Dirty, Private_Hugetlb.
+ _private_re=re.compile(br"\nPrivate.*:\s+(\d+)"),
+ _pss_re=re.compile(br"\nPss\:\s+(\d+)"),
+ _swap_re=re.compile(br"\nSwap\:\s+(\d+)")):
basic_mem = self.memory_info()
# Note: using 3 regexes is faster than reading the file
# line by line.
diff --git a/server/www/packages/packages-windows/x86/psutil/_psosx.py b/server/www/packages/packages-windows/x86/psutil/_psosx.py
index 4c97af7..fbfedf3 100644
--- a/server/www/packages/packages-windows/x86/psutil/_psosx.py
+++ b/server/www/packages/packages-windows/x86/psutil/_psosx.py
@@ -2,7 +2,7 @@
# Use of this source code is governed by a BSD-style license that can be
# found in the LICENSE file.
-"""OSX platform implementation."""
+"""macOS platform implementation."""
import contextlib
import errno
@@ -122,7 +122,7 @@ def virtual_memory():
total, active, inactive, wired, free = cext.virtual_mem()
avail = inactive + free
used = active + inactive + wired
- percent = usage_percent((total - avail), total, _round=1)
+ percent = usage_percent((total - avail), total, round_=1)
return svmem(total, avail, percent, used, free,
active, inactive, wired)
@@ -130,7 +130,7 @@ def virtual_memory():
def swap_memory():
"""Swap system memory as a (total, used, free, sin, sout) tuple."""
total, used, free, sin, sout = cext.swap_mem()
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent, sin, sout)
@@ -174,7 +174,7 @@ def cpu_stats():
def cpu_freq():
"""Return CPU frequency.
- On OSX per-cpu frequency is not supported.
+ On macOS per-cpu frequency is not supported.
Also, the returned frequency never changes, see:
https://arstechnica.com/civis/viewtopic.php?f=19&t=465002
"""
@@ -213,8 +213,7 @@ def disk_partitions(all=False):
def sensors_battery():
- """Return battery information.
- """
+ """Return battery information."""
try:
percent, minsleft, power_plugged = cext.sensors_battery()
except NotImplementedError:
@@ -241,7 +240,7 @@ net_if_addrs = cext_posix.net_if_addrs
def net_connections(kind='inet'):
"""System-wide network connections."""
- # Note: on OSX this will fail with AccessDenied unless
+ # Note: on macOS this will fail with AccessDenied unless
# the process is owned by root.
ret = []
for pid in pids():
@@ -262,12 +261,18 @@ def net_if_stats():
names = net_io_counters().keys()
ret = {}
for name in names:
- mtu = cext_posix.net_if_mtu(name)
- isup = cext_posix.net_if_flags(name)
- duplex, speed = cext_posix.net_if_duplex_speed(name)
- if hasattr(_common, 'NicDuplex'):
- duplex = _common.NicDuplex(duplex)
- ret[name] = _common.snicstats(isup, duplex, speed, mtu)
+ try:
+ mtu = cext_posix.net_if_mtu(name)
+ isup = cext_posix.net_if_flags(name)
+ duplex, speed = cext_posix.net_if_duplex_speed(name)
+ except OSError as err:
+ # https://github.com/giampaolo/psutil/issues/1279
+ if err.errno != errno.ENODEV:
+ raise
+ else:
+ if hasattr(_common, 'NicDuplex'):
+ duplex = _common.NicDuplex(duplex)
+ ret[name] = _common.snicstats(isup, duplex, speed, mtu)
return ret
@@ -304,17 +309,16 @@ def users():
def pids():
ls = cext.pids()
if 0 not in ls:
- # On certain OSX versions pids() C doesn't return PID 0 but
+ # On certain macOS versions pids() C doesn't return PID 0 but
# "ps" does and the process is querable via sysctl():
# https://travis-ci.org/giampaolo/psutil/jobs/309619941
try:
Process(0).create_time()
+ ls.insert(0, 0)
except NoSuchProcess:
- return False
+ pass
except AccessDenied:
- ls.append(0)
- else:
- ls.append(0)
+ ls.insert(0, 0)
return ls
@@ -335,6 +339,8 @@ def wrap_exceptions(fun):
if err.errno in (errno.EPERM, errno.EACCES):
raise AccessDenied(self.pid, self._name)
raise
+ except cext.ZombieProcessError:
+ raise ZombieProcess(self.pid, self._name, self._ppid)
return wrapper
@@ -558,8 +564,7 @@ class Process(object):
@wrap_exceptions
def threads(self):
- with catch_zombie(self):
- rawlist = cext.proc_threads(self.pid)
+ rawlist = cext.proc_threads(self.pid)
retlist = []
for thread_id, utime, stime in rawlist:
ntuple = _common.pthread(thread_id, utime, stime)
@@ -568,5 +573,4 @@ class Process(object):
@wrap_exceptions
def memory_maps(self):
- with catch_zombie(self):
- return cext.proc_memory_maps(self.pid)
+ return cext.proc_memory_maps(self.pid)
diff --git a/server/www/packages/packages-windows/x86/psutil/_psposix.py b/server/www/packages/packages-windows/x86/psutil/_psposix.py
index 6bb8444..9c3fac2 100644
--- a/server/www/packages/packages-windows/x86/psutil/_psposix.py
+++ b/server/www/packages/packages-windows/x86/psutil/_psposix.py
@@ -156,7 +156,7 @@ def disk_usage(path):
# User usage percent compared to the total amount of space
# the user can use. This number would be higher if compared
# to root's because the user has less space (usually -5%).
- usage_percent_user = usage_percent(used, total_user, _round=1)
+ usage_percent_user = usage_percent(used, total_user, round_=1)
# NB: the percentage is -5% than what shown by df due to
# reserved blocks that we are currently not considering:
diff --git a/server/www/packages/packages-windows/x86/psutil/_pssunos.py b/server/www/packages/packages-windows/x86/psutil/_pssunos.py
index 5471d5a..e2f33a3 100644
--- a/server/www/packages/packages-windows/x86/psutil/_pssunos.py
+++ b/server/www/packages/packages-windows/x86/psutil/_pssunos.py
@@ -79,7 +79,11 @@ proc_info_map = dict(
nice=4,
num_threads=5,
status=6,
- ttynr=7)
+ ttynr=7,
+ uid=8,
+ euid=9,
+ gid=10,
+ egid=11)
# =====================================================================
@@ -127,7 +131,7 @@ def virtual_memory():
# note: there's no difference on Solaris
free = avail = os.sysconf('SC_AVPHYS_PAGES') * PAGE_SIZE
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return svmem(total, avail, percent, used, free)
@@ -159,7 +163,7 @@ def swap_memory():
total += int(int(t) * 512)
free += int(int(f) * 512)
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent,
sin * PAGE_SIZE, sout * PAGE_SIZE)
@@ -394,7 +398,10 @@ class Process(object):
@memoize_when_activated
def _proc_cred(self):
- return cext.proc_cred(self.pid, self._procfs_path)
+ @wrap_exceptions
+ def proc_cred(self):
+ return cext.proc_cred(self.pid, self._procfs_path)
+ return proc_cred(self)
@wrap_exceptions
def name(self):
@@ -432,27 +439,10 @@ class Process(object):
@wrap_exceptions
def nice_get(self):
- # Note #1: for some reason getpriority(3) return ESRCH (no such
- # process) for certain low-pid processes, no matter what (even
- # as root).
- # The process actually exists though, as it has a name,
- # creation time, etc.
- # The best thing we can do here appears to be raising AD.
- # Note: tested on Solaris 11; on Open Solaris 5 everything is
- # fine.
- #
- # Note #2: we also can get niceness from /proc/pid/psinfo
- # but it's wrong, see:
- # https://github.com/giampaolo/psutil/issues/1082
- try:
- return cext_posix.getpriority(self.pid)
- except EnvironmentError as err:
- # 48 is 'operation not supported' but errno does not expose
- # it. It occurs for low system pids.
- if err.errno in (errno.ENOENT, errno.ESRCH, 48):
- if pid_exists(self.pid):
- raise AccessDenied(self.pid, self._name)
- raise
+ # Note #1: getpriority(3) doesn't work for realtime processes.
+ # Psinfo is what ps uses, see:
+ # https://github.com/giampaolo/psutil/issues/1194
+ return self._proc_basic_info()[proc_info_map['nice']]
@wrap_exceptions
def nice_set(self, value):
@@ -471,12 +461,22 @@ class Process(object):
@wrap_exceptions
def uids(self):
- real, effective, saved, _, _, _ = self._proc_cred()
+ try:
+ real, effective, saved, _, _, _ = self._proc_cred()
+ except AccessDenied:
+ real = self._proc_basic_info()[proc_info_map['uid']]
+ effective = self._proc_basic_info()[proc_info_map['euid']]
+ saved = None
return _common.puids(real, effective, saved)
@wrap_exceptions
def gids(self):
- _, _, _, real, effective, saved = self._proc_cred()
+ try:
+ _, _, _, real, effective, saved = self._proc_cred()
+ except AccessDenied:
+ real = self._proc_basic_info()[proc_info_map['gid']]
+ effective = self._proc_basic_info()[proc_info_map['egid']]
+ saved = None
return _common.puids(real, effective, saved)
@wrap_exceptions
diff --git a/server/www/packages/packages-windows/x86/psutil/_psutil_windows.cp37-win32.pyd b/server/www/packages/packages-windows/x86/psutil/_psutil_windows.cp37-win32.pyd
new file mode 100644
index 0000000..feb16eb
Binary files /dev/null and b/server/www/packages/packages-windows/x86/psutil/_psutil_windows.cp37-win32.pyd differ
diff --git a/server/www/packages/packages-windows/x86/psutil/_psutil_windows.pyd b/server/www/packages/packages-windows/x86/psutil/_psutil_windows.pyd
deleted file mode 100644
index be0bfa9..0000000
Binary files a/server/www/packages/packages-windows/x86/psutil/_psutil_windows.pyd and /dev/null differ
diff --git a/server/www/packages/packages-windows/x86/psutil/_pswindows.py b/server/www/packages/packages-windows/x86/psutil/_pswindows.py
index b6c58c9..18651d6 100644
--- a/server/www/packages/packages-windows/x86/psutil/_pswindows.py
+++ b/server/www/packages/packages-windows/x86/psutil/_pswindows.py
@@ -9,6 +9,7 @@ import errno
import functools
import os
import sys
+import time
from collections import namedtuple
from . import _common
@@ -78,7 +79,6 @@ __extra__all__ = [
# =====================================================================
CONN_DELETE_TCB = "DELETE_TCB"
-WAIT_TIMEOUT = 0x00000102 # 258 in decimal
ACCESS_DENIED_ERRSET = frozenset([errno.EPERM, errno.EACCES,
cext.ERROR_ACCESS_DENIED])
NO_SUCH_SERVICE_ERRSET = frozenset([cext.ERROR_INVALID_NAME,
@@ -200,7 +200,7 @@ def py2_strencode(s):
if isinstance(s, str):
return s
else:
- return s.encode(ENCODING, errors=ENCODING_ERRS)
+ return s.encode(ENCODING, ENCODING_ERRS)
# =====================================================================
@@ -217,7 +217,7 @@ def virtual_memory():
avail = availphys
free = availphys
used = total - avail
- percent = usage_percent((total - avail), total, _round=1)
+ percent = usage_percent((total - avail), total, round_=1)
return svmem(total, avail, percent, used, free)
@@ -227,7 +227,7 @@ def swap_memory():
total = mem[2]
free = mem[3]
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sswap(total, used, free, percent, 0, 0)
@@ -247,7 +247,7 @@ def disk_usage(path):
path = path.decode(ENCODING, errors="strict")
total, free = cext.disk_usage(path)
used = total - free
- percent = usage_percent(used, total, _round=1)
+ percent = usage_percent(used, total, round_=1)
return _common.sdiskusage(total, used, free, percent)
@@ -288,7 +288,7 @@ def cpu_count_logical():
def cpu_count_physical():
- """Return the number of physical CPUs in the system."""
+ """Return the number of physical CPU cores in the system."""
return cext.cpu_count_phys()
@@ -792,18 +792,43 @@ class Process(object):
if timeout is None:
cext_timeout = cext.INFINITE
else:
- # WaitForSingleObject() expects time in milliseconds
+ # WaitForSingleObject() expects time in milliseconds.
cext_timeout = int(timeout * 1000)
+
+ timer = getattr(time, 'monotonic', time.time)
+ stop_at = timer() + timeout if timeout is not None else None
+
+ try:
+ # Exit code is supposed to come from GetExitCodeProcess().
+ # May also be None if OpenProcess() failed with
+ # ERROR_INVALID_PARAMETER, meaning PID is already gone.
+ exit_code = cext.proc_wait(self.pid, cext_timeout)
+ except cext.TimeoutExpired:
+ # WaitForSingleObject() returned WAIT_TIMEOUT. Just raise.
+ raise TimeoutExpired(timeout, self.pid, self._name)
+ except cext.TimeoutAbandoned:
+ # WaitForSingleObject() returned WAIT_ABANDONED, see:
+ # https://github.com/giampaolo/psutil/issues/1224
+ # We'll just rely on the internal polling and return None
+ # when the PID disappears. Subprocess module does the same
+ # (return None):
+ # https://github.com/python/cpython/blob/
+ # be50a7b627d0aa37e08fa8e2d5568891f19903ce/
+ # Lib/subprocess.py#L1193-L1194
+ exit_code = None
+
+ # At this point WaitForSingleObject() returned WAIT_OBJECT_0,
+ # meaning the process is gone. Stupidly there are cases where
+ # its PID may still stick around so we do a further internal
+ # polling.
+ delay = 0.0001
while True:
- ret = cext.proc_wait(self.pid, cext_timeout)
- if ret == WAIT_TIMEOUT:
- raise TimeoutExpired(timeout, self.pid, self._name)
- if pid_exists(self.pid):
- if timeout is None:
- continue
- else:
- raise TimeoutExpired(timeout, self.pid, self._name)
- return ret
+ if not pid_exists(self.pid):
+ return exit_code
+ if stop_at and timer() >= stop_at:
+ raise TimeoutExpired(timeout, pid=self.pid, name=self._name)
+ time.sleep(delay)
+ delay = min(delay * 2, 0.04) # incremental delay
@wrap_exceptions
def username(self):
diff --git a/server/www/teleport/.idea/teleport.iml b/server/www/teleport/.idea/teleport.iml
index d58057b..0059564 100644
--- a/server/www/teleport/.idea/teleport.iml
+++ b/server/www/teleport/.idea/teleport.iml
@@ -7,7 +7,7 @@
-
+