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

zopefoundation / Products.CMFCore / 6246931310

20 Sep 2023 09:54AM UTC coverage: 86.008% (-0.3%) from 86.266%
6246931310

Pull #131

github

mauritsvanrees
gha: don't need setup-python on 27 as we use the 27 container.
Pull Request #131: Make decodeFolderFilter and encodeFolderFilter non-public.

2466 of 3689 branches covered (0.0%)

Branch coverage included in aggregate %.

6 of 6 new or added lines in 1 file covered. (100.0%)

17297 of 19289 relevant lines covered (89.67%)

0.9 hits per line

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

97.3
/src/Products/CMFCore/FSMetadata.py
1
##############################################################################
2
#
3
# Copyright (c) 2003 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
"""Handles reading the properties for an object that comes from the filesystem.
1✔
14
"""
15

16
import logging
1✔
17
import re
1✔
18
from os.path import exists
1✔
19

20
from six.moves.configparser import ConfigParser
1✔
21

22

23
logger = logging.getLogger('CMFCore.FSMetadata')
1✔
24

25

26
class CMFConfigParser(ConfigParser):
1✔
27
    """ This our wrapper around ConfigParser to
28
    solve a few minor niggles with the code """
29
    # adding in a space so that names can contain spaces
30
    OPTCRE = re.compile(
1✔
31
        r'(?P<option>[]\-[ \w_.*,(){}]+)'     # noqa stuff found by IvL
32
        r'[ \t]*(?P<vi>[:=])[ \t]*'           # any number of space/tab,
33
                                              # followed by separator
34
                                              # (either : or =), followed
35
                                              # by any # space/tab
36
        r'(?P<value>.*)$')                    # everything up to eol
37

38
    def optionxform(self, optionstr):
1✔
39
        """
40
        Stop converting the key to lower case, very annoying for security etc
41
        """
42
        return optionstr.strip()
1✔
43

44

45
class FSMetadata:
1✔
46
    # public API
47
    def __init__(self, filename):
1✔
48
        self._filename = filename
1✔
49

50
    def read(self):
1✔
51
        """ Find the files to read, either the old security and
52
        properties type or the new metadata type """
53
        filename = self._filename + '.metadata'
1✔
54
        if exists(filename):
1✔
55
            # found the new type, lets use that
56
            self._readMetadata()
1✔
57
        else:
58
            self._properties = {}
1✔
59
            self._security = {}
1✔
60

61
    def getProxyRoles(self):
1✔
62
        """ Returns the proxy roles """
63
        if self.getProperties():
1✔
64
            pxy = self.getProperties().get('proxy')
1✔
65
            if pxy:
1✔
66
                return [r.strip() for r in pxy.split(',') if r.strip()]
1✔
67
        return []
1✔
68

69
    def getSecurity(self):
1✔
70
        """ Gets the security settings """
71
        return self._security
1✔
72

73
    def getProperties(self):
1✔
74
        """ Gets the properties settings """
75
        return self._properties
1✔
76

77
    # private API
78
    def _readMetadata(self):
1✔
79
        """ Read the new file format using ConfigParser """
80
        self._properties = {}
1✔
81
        self._security = {}
1✔
82

83
        try:
1✔
84
            cfg = CMFConfigParser()
1✔
85
            cfg.read(self._filename + '.metadata')
1✔
86

87
            # the two sections we care about
88
            self._properties = self._getSectionDict(cfg, 'default')
1✔
89
            self._security = self._getSectionDict(cfg, 'security',
1✔
90
                                                  self._securityParser)
91
        except Exception:
1✔
92
            logger.exception('Error parsing .metadata file')
1✔
93

94
        # to add in a new value such as proxy roles,
95
        # just add in the section, call it using getSectionDict
96
        # if you need a special parser for some whacky
97
        # config, then just pass through a special parser
98

99
    def _nullParser(self, data):
1✔
100
        """
101
        This is the standard rather boring null parser that does very little
102
        """
103
        return data
1✔
104

105
    def _securityParser(self, data):
1✔
106
        """ A specific parser for security lines
107

108
        Security lines must be of the format
109

110
        Permission = (0|1):Role[,Role...]
111

112
        Where 0|1 is the acquire permission setting
113
        and Role is the roles for this permission
114
        eg: 1:Manager or 0:Manager,Anonymous
115
        """
116
        if data.find(':') < 1:
1!
117
            raise ValueError('The security declaration of file ' +
×
118
                             '%r is in the wrong format' % self._filename)
119

120
        acquire, roles = data.split(':')
1✔
121
        roles = [r.strip() for r in roles.split(',') if r.strip()]
1✔
122
        return (int(acquire), roles)
1✔
123

124
    def _getSectionDict(self, cfg, section, parser=None):
1✔
125
        """
126
        Get a section and put it into a dict, mostly a convenience
127
        function around the ConfigParser
128

129
        Note: the parser is a function to parse each value, so you can
130
        have custom values for the key value pairs
131
        """
132
        if parser is None:
1✔
133
            parser = self._nullParser
1✔
134

135
        props = {}
1✔
136
        if cfg.has_section(section):
1✔
137
            for opt in cfg.options(section):
1✔
138
                props[opt] = parser(cfg.get(section, opt))
1✔
139
            return props
1✔
140

141
        # we need to return None if we have none to be compatible
142
        # with existing API
143
        return None
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