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

zopefoundation / grokcore.startup / 13748205778

15 Jan 2025 07:31AM UTC coverage: 31.667% (+0.1%) from 31.551%
13748205778

push

github

web-flow
Update Python version support. (#15)

* Drop support for Python 3.7, 3.8.
* Add support for Python 3.13.

7 of 30 branches covered (23.33%)

Branch coverage included in aggregate %.

0 of 3 new or added lines in 2 files covered. (0.0%)

1 existing line in 1 file now uncovered.

50 of 150 relevant lines covered (33.33%)

0.33 hits per line

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

0.0
/src/grokcore/startup/debug.py
1
##############################################################################
2
#
3
# Copyright (c) 2006-2012 Zope Foundation and Contributors.
4
# All Rights Reserved.
5
#
6
# This software is subject to the provisions of the Zope Public License,
7
# Version 2.1 (ZPL).  A copy of the ZPL should accompany this distribution.
8
# THIS SOFTWARE IS PROVIDED "AS IS" AND ANY AND ALL EXPRESS OR IMPLIED
9
# WARRANTIES ARE DISCLAIMED, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED
10
# WARRANTIES OF TITLE, MERCHANTABILITY, AGAINST INFRINGEMENT, AND FITNESS
11
# FOR A PARTICULAR PURPOSE.
12
#
13
##############################################################################
14
import os.path
×
15
import textwrap
×
16
from pprint import pprint
×
17

18
import transaction
×
19
import zope.app.debug
×
20
import zope.app.wsgi
×
NEW
21
from IPython.terminal.embed import InteractiveShellEmbed  # noqa: T100
×
22
from zope.component import getMultiAdapter
×
23
from zope.component import getUtility
×
24
from zope.securitypolicy.zopepolicy import settingsForObject
×
25

26

NEW
27
shell = InteractiveShellEmbed()  # noqa: T100 trace found
×
28

29
PATH_SEP = '/'
×
30

31

32
class GrokDebug:
×
33

34
    def __init__(self, debugger):
×
35
        debugger = debugger
×
36
        self.app = debugger
×
37
        self.root = debugger.root()
×
38
        self.context = self.root
×
39

40
    def get_start_context(self, path):
×
41
        if path.startswith(PATH_SEP):
×
42
            context = self.root
×
43
        else:
44
            # relative path
45
            context = self.context
×
46
        return context
×
47

48
    def _get_object_names(self, context):
×
49
        return [obj.__name__ for obj in context.values()]
×
50

51
    def ns(self):
×
52
        """Return namespace dictionary.
53

54
        To be used for updating namespace of commands available
55
        for user in shell.
56
        """
57
        return dict(lsg=self.ls,
×
58
                    cdg=self.cd,
59
                    pwdg=self.pwd,
60
                    app=self.app,
61
                    root=self.root,
62
                    ctx=self.ctx,
63
                    sec=self.get_security_settings,
64
                    gu=getUtility,
65
                    gma=getMultiAdapter,
66
                    sync=self.sync,
67
                    pby=self.providedBy,
68
                    commit=transaction.commit)
69

70
    def update_ns(self):
×
71
        shell.user_ns.update(self.ns())
×
72

73
    def get_security_settings(self, path):
×
74
        pprint(
×
75
            settingsForObject(
76
                get_context_by_path(self.get_start_context(path), path)))
77

78
    def sync(self):
×
79
        self.root._p_jar.sync()
×
80

81
    def ls(self, path=None):
×
82
        """List objects.
83

84
        This command is bound to `lsg` in IPython shell.
85

86
        Without `path` parameter list objects in current container,
87
        which is available as `ctx` from IPython shell.
88

89
        `path` can be relative or absolute.
90

91
        To use autocompletion of path command should be invoked
92
        with prepended semicolon in ipython shell as
93
        ;lsg /path
94
        """
95
        if path is None:
×
96
            return self._get_object_names(self.context)
×
97

98
        context = get_context_by_path(self.get_start_context(path), path)
×
99
        return self._get_object_names(context)
×
100

101
    def cd(self, path):
×
102
        """cd to specified path.
103

104
        Bound to `cdg` in IPython shell.
105
        `path` can be relative or absolute.
106

107
        To use autocompletion of path command should be invoked
108
        with prepended semicolon in ipython shell as
109
        ;cdg /path
110
        """
111
        if path.strip() == '..':
×
112
            self.context = self.context.__parent__
×
113
            self.update_ns()
×
114
            return self.pwd
×
115

116
        # cd
117
        self.context = get_context_by_path(self.get_start_context(path), path)
×
118
        self.update_ns()
×
119
        return self.pwd
×
120

121
    @property
×
122
    def pwd(self):
×
123
        """Print absolute path to current context object.
124

125
        Bound to `pwdg` in IPython shell
126
        """
127
        res = []
×
128
        obj = self.context
×
129
        while obj is not None:
×
130
            name = obj.__name__
×
131
            if name is not None and name:
×
132
                res.append(name)
×
133
            obj = obj.__parent__
×
134

135
        if not res:
×
136
            return PATH_SEP
×
137

138
        res = PATH_SEP.join(reversed(res))
×
139
        if not res.startswith(PATH_SEP):
×
140
            return PATH_SEP + res
×
141

142
        return res
×
143

144
    @property
×
145
    def ctx(self):
×
146
        """Return current context object.
147

148
        Bound to `ctx` in IPython shell
149
        """
150
        return self.context
×
151

152
    def providedBy(self, obj=None):
×
153
        if not obj:
×
154
            obj = self.ctx
×
155
        return list(zope.interface.providedBy(obj))
×
156

157

158
def get_context_by_path(context, path):
×
159
    for name in (p for p in path.split(PATH_SEP) if p):
×
160
        context = context[name]
×
161
    return context
×
162

163

164
def path_completer(self, event):
×
165
    """TAB path completer for `cdg` and `lsg` commands."""
166
    relpath = event.symbol
×
167

168
    context = grokd.get_start_context(relpath)  # noqa: F821 undefined name
×
169

170
    # ends with '/'
171
    if relpath.endswith(PATH_SEP):
×
172
        context = get_context_by_path(context, relpath)
×
173
        return [relpath + obj.__name__ for obj in context.values()]
×
174

175
    head, tail = os.path.split(relpath)
×
176
    if head and not head.endswith(PATH_SEP):
×
177
        head += PATH_SEP
×
178
    context = get_context_by_path(context, head)
×
179

180
    return [
×
181
        head + obj.__name__ for obj in context.values()
182
        if obj.__name__.startswith(tail)
183
    ]
184

185

186
def ipython_debug_prompt(debugger):
×
187
    grokd = GrokDebug(debugger)
×
188
    banner = textwrap.dedent("""\
×
189
        IPython shell for Grok.
190

191
        Bound object names:
192
        -------------------
193
          root
194
          ctx
195

196
        Bound command names:
197
        --------------------
198
          cdg / ;cdg
199
          lsg / ;lsg
200
          sec / ;sec
201
          gu  / ;gu
202
          gma / ;gma
203
          pby (providedBy)
204
          pwdg
205
          sync
206
          commit
207
        """)
208

209
    shell.user_ns.update(grokd.ns())
×
210
    shell.banner2 = banner
×
211
    shell.set_hook('complete_command', path_completer, re_key='.*cdg|.*lsg')
×
212
    shell(local_ns=grokd.ns())
×
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