• Home
  • Features
  • Pricing
  • Docs
  • Announcements
  • Sign In

zopefoundation / Zope / 3956162881

pending completion
3956162881

push

github

Michael Howitz
Update to deprecation warning free releases.

4401 of 7036 branches covered (62.55%)

Branch coverage included in aggregate %.

27161 of 31488 relevant lines covered (86.26%)

0.86 hits per line

Source File
Press 'n' to go to next uncovered line, 'b' for previous

82.47
/src/App/ImageFile.py
1
##############################################################################
2
#
3
# Copyright (c) 2002 Zope Foundation and Contributors.
4
#
5
# This software is subject to the provisions of the Zope Public License,
6
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
7
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
8
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
9
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
10
# FOR A PARTICULAR PURPOSE.
11
#
12
##############################################################################
13
"""Image object that is stored in a file"""
1✔
14

15
import os.path
1✔
16
import stat
1✔
17
import time
1✔
18
import warnings
1✔
19

20
import Zope2
1✔
21
from AccessControl.class_init import InitializeClass
1✔
22
from AccessControl.SecurityInfo import ClassSecurityInfo
1✔
23
from Acquisition import Explicit
1✔
24
from App.Common import package_home
1✔
25
from App.config import getConfiguration
1✔
26
from DateTime.DateTime import DateTime
1✔
27
from zope.contenttype import guess_content_type
1✔
28
from zope.datetime import rfc1123_date
1✔
29
from ZPublisher.Iterators import filestream_iterator
1✔
30

31

32
PREFIX = os.path.realpath(
1✔
33
    os.path.join(os.path.dirname(Zope2.__file__), os.path.pardir))
34

35
NON_PREFIX_WARNING = ('Assuming image location to be present in the Zope2 '
1✔
36
                      'distribution. This is deprecated and might lead to '
37
                      'broken code if the directory in question is moved '
38
                      'to another distribution. Please provide either an '
39
                      'absolute file system path or a prefix. Support for '
40
                      'relative filenames without a prefix might be '
41
                      'dropped in a future Zope2 release.')
42

43

44
class ImageFile(Explicit):
1✔
45
    """Image objects stored in external files."""
46

47
    security = ClassSecurityInfo()
1✔
48

49
    def __init__(self, path, _prefix=None):
1✔
50
        if _prefix is None:
1✔
51
            _prefix = PREFIX
1✔
52
            if not os.path.isabs(path):
1✔
53
                warnings.warn(NON_PREFIX_WARNING, UserWarning, 2)
1✔
54
        elif not isinstance(_prefix, str):
1✔
55
            _prefix = package_home(_prefix)
1✔
56
        # _prefix is ignored if path is absolute
57
        path = os.path.join(_prefix, path)
1✔
58
        self.path = path
1✔
59
        if getConfiguration().debug_mode:
1✔
60
            # In development mode, a shorter time is handy
61
            max_age = 60  # One minute
1✔
62
        else:
63
            # A longer time reduces latency in production mode
64
            max_age = 3600  # One hour
1✔
65
        self.cch = 'public,max-age=%d' % max_age
1✔
66

67
        # First try to get the content_type by name
68
        content_type, enc = guess_content_type(path, default='failed')
1✔
69

70
        if content_type == 'failed':
1!
71
            # This failed, lets look into the file content
72
            with open(path, 'rb') as img:
×
73
                data = img.read(1024)  # 1k should be enough
×
74

75
            content_type, enc = guess_content_type(path, data)
×
76

77
        if content_type:
1!
78
            self.content_type = content_type
1✔
79
        else:
80
            ext = os.path.splitext(path)[-1].replace('.', '')
×
81
            self.content_type = 'image/%s' % ext
×
82

83
        self.__name__ = os.path.split(path)[-1]
1✔
84
        stat_info = os.stat(path)
1✔
85
        self.size = stat_info[stat.ST_SIZE]
1✔
86
        self.lmt = float(stat_info[stat.ST_MTIME]) or time.time()
1✔
87
        self.lmh = rfc1123_date(self.lmt)
1✔
88

89
    def index_html(self, REQUEST, RESPONSE):
1✔
90
        """Default document"""
91
        # HTTP If-Modified-Since header handling. This is duplicated
92
        # from OFS.Image.Image - it really should be consolidated
93
        # somewhere...
94
        RESPONSE.setHeader('Content-Type', self.content_type)
1✔
95
        RESPONSE.setHeader('Last-Modified', self.lmh)
1✔
96
        RESPONSE.setHeader('Cache-Control', self.cch)
1✔
97
        header = REQUEST.get_header('If-Modified-Since', None)
1✔
98
        if header is not None:
1✔
99
            header = header.split(';')[0]
1✔
100
            # Some proxies seem to send invalid date strings for this
101
            # header. If the date string is not valid, we ignore it
102
            # rather than raise an error to be generally consistent
103
            # with common servers such as Apache (which can usually
104
            # understand the screwy date string as a lucky side effect
105
            # of the way they parse it).
106
            try:
1✔
107
                mod_since = int(DateTime(header).timeTime())
1✔
108
            except Exception:
×
109
                mod_since = None
×
110
            if mod_since is not None:
1!
111
                if getattr(self, 'lmt', None):
1!
112
                    last_mod = int(self.lmt)
1✔
113
                else:
114
                    last_mod = int(0)
×
115
                if last_mod > 0 and last_mod <= mod_since:
1!
116
                    RESPONSE.setHeader('Content-Length', '0')
1✔
117
                    RESPONSE.setStatus(304)
1✔
118
                    return ''
1✔
119

120
        RESPONSE.setHeader('Content-Length', str(self.size).replace('L', ''))
1✔
121
        return filestream_iterator(self.path, mode='rb')
1✔
122

123
    @security.public
1✔
124
    def HEAD(self, REQUEST, RESPONSE):
1✔
125
        """ """
126
        RESPONSE.setHeader('Content-Type', self.content_type)
×
127
        RESPONSE.setHeader('Last-Modified', self.lmh)
×
128
        return ''
×
129

130
    def __len__(self):
1✔
131
        # This is bogus and needed because of the way Python tests truth.
132
        return 1
×
133

134
    def __str__(self):
1✔
135
        return '<img src="%s" alt="" />' % self.__name__
1✔
136

137

138
InitializeClass(ImageFile)
1✔
STATUS · Troubleshooting · Open an Issue · Sales · Support · CAREERS · ENTERPRISE · START FREE · SCHEDULE DEMO
ANNOUNCEMENTS · TWITTER · TOS & SLA · Supported CI Services · What's a CI service? · Automated Testing

© 2025 Coveralls, Inc