Products.PythonScripts 5.0__py3-none-any.whl → 5.2__py3-none-any.whl

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -16,7 +16,9 @@ This product provides support for Script objects containing restricted
16
16
  Python code.
17
17
  """
18
18
 
19
+ import importlib.abc
19
20
  import importlib.util
21
+ import linecache
20
22
  import marshal
21
23
  import os
22
24
  import re
@@ -56,7 +58,7 @@ LOG = getLogger('PythonScripts')
56
58
  Python_magic = importlib.util.MAGIC_NUMBER
57
59
 
58
60
  # This should only be incremented to force recompilation.
59
- Script_magic = 4
61
+ Script_magic = 5
60
62
  _log_complaint = (
61
63
  'Some of your Scripts have stale code cached. Since Zope cannot'
62
64
  ' use this code, startup will be slightly slower until these Scripts'
@@ -97,6 +99,17 @@ def manage_addPythonScript(self, id, title='', file=None, REQUEST=None,
97
99
  return ''
98
100
 
99
101
 
102
+ class PythonScriptLoader(importlib.abc.Loader):
103
+ """PEP302 loader to display source code in tracebacks
104
+ """
105
+
106
+ def __init__(self, source):
107
+ self._source = source
108
+
109
+ def get_source(self, name):
110
+ return self._source
111
+
112
+
100
113
  class PythonScript(Script, Historical, Cacheable):
101
114
  """Web-callable scripts written in a safe subset of Python.
102
115
 
@@ -179,12 +192,12 @@ class PythonScript(Script, Historical, Cacheable):
179
192
  if self.wl_isLocked():
180
193
  raise ResourceLockedError('The script is locked via WebDAV.')
181
194
 
195
+ if not file:
196
+ return self.ZPythonScriptHTML_editForm(
197
+ self, REQUEST,
198
+ manage_tabs_message='No file specified',
199
+ manage_tabs_type='warning')
182
200
  if not isinstance(file, str):
183
- if not file:
184
- return self.ZPythonScriptHTML_editForm(
185
- self, REQUEST,
186
- manage_tabs_message='No file specified',
187
- manage_tabs_type='warning')
188
201
  file = file.read()
189
202
 
190
203
  self.write(file)
@@ -234,7 +247,7 @@ class PythonScript(Script, Historical, Cacheable):
234
247
  self._params,
235
248
  body=self._body or 'pass',
236
249
  name=self.id,
237
- filename=self.meta_type,
250
+ filename=getattr(self, '_filepath', None) or self.get_filepath(),
238
251
  globalize=bind_names)
239
252
 
240
253
  code = compile_result.code
@@ -261,6 +274,7 @@ class PythonScript(Script, Historical, Cacheable):
261
274
  fc.co_argcount)
262
275
  self.Python_magic = Python_magic
263
276
  self.Script_magic = Script_magic
277
+ linecache.clearcache()
264
278
  self._v_change = 0
265
279
 
266
280
  def _newfun(self, code):
@@ -331,6 +345,8 @@ class PythonScript(Script, Historical, Cacheable):
331
345
  PythonScriptTracebackSupplement, self, -1)
332
346
  safe_globals['__file__'] = getattr(
333
347
  self, '_filepath', None) or self.get_filepath()
348
+ safe_globals['__loader__'] = PythonScriptLoader(self._body)
349
+
334
350
  function = types.FunctionType(
335
351
  function_code, safe_globals, None, function_argument_definitions)
336
352
 
@@ -556,6 +572,7 @@ InitializeClass(PythonScript)
556
572
 
557
573
  class PythonScriptTracebackSupplement:
558
574
  """Implementation of ITracebackSupplement"""
575
+
559
576
  def __init__(self, script, line=0):
560
577
  self.object = script
561
578
  # If line is set to -1, it means to use tb_lineno.
@@ -43,7 +43,7 @@ def initialize(context):
43
43
  icon='www/pyscript.gif',
44
44
  )
45
45
 
46
- global _m
46
+ global _m # noqa: F824
47
47
  _m['recompile'] = recompile
48
48
  _m['recompile__roles__'] = ('Manager',)
49
49
 
@@ -15,6 +15,7 @@ import contextlib
15
15
  import io
16
16
  import os
17
17
  import sys
18
+ import traceback
18
19
  import unittest
19
20
  import warnings
20
21
  from urllib.error import HTTPError
@@ -241,7 +242,8 @@ class TestPythonScriptNoAq(PythonScriptTestBase):
241
242
  self.assertEqual(ps.read(), ps.manage_DAVget())
242
243
 
243
244
  def test_PUT_native_string(self):
244
- ps = makerequest(self._filePS('complete'))
245
+ container = DummyFolder('container')
246
+ ps = makerequest(self._filePS('complete').__of__(container))
245
247
  self.assertEqual(ps.title, 'This is a title')
246
248
  self.assertEqual(ps.body(), 'print(foo+bar+baz)\nreturn printed\n')
247
249
  self.assertEqual(ps.params(), 'foo, bar, baz=1')
@@ -265,7 +267,8 @@ return \xe4\xe9\xee\xf6\xfc
265
267
  self.assertEqual(ps.params(), 'oops')
266
268
 
267
269
  def test_PUT_bytes(self):
268
- ps = makerequest(self._filePS('complete'))
270
+ container = DummyFolder('container')
271
+ ps = makerequest(self._filePS('complete').__of__(container))
269
272
  self.assertEqual(ps.title, 'This is a title')
270
273
  self.assertEqual(ps.body(), 'print(foo+bar+baz)\nreturn printed\n')
271
274
  self.assertEqual(ps.params(), 'foo, bar, baz=1')
@@ -588,3 +591,46 @@ class PythonScriptBrowserTests(FunctionalTestCase):
588
591
 
589
592
  # Cleanup
590
593
  noSecurityManager()
594
+
595
+
596
+ class TestTraceback(FunctionalTestCase, PythonScriptTestBase):
597
+
598
+ def _format_exception(self):
599
+ return "".join(traceback.format_exception(*sys.exc_info()))
600
+
601
+ def test_source_code_in_traceback(self):
602
+ ps = self._newPS("1 / 0")
603
+ try:
604
+ ps()
605
+ except ZeroDivisionError:
606
+ formatted_exception = self._format_exception()
607
+ self.assertIn("1 / 0", formatted_exception)
608
+
609
+ ps.write("2 / 0")
610
+ try:
611
+ ps()
612
+ except ZeroDivisionError:
613
+ formatted_exception = self._format_exception()
614
+ self.assertIn("2 / 0", formatted_exception)
615
+
616
+ def test_multiple_scripts_in_traceback(self):
617
+ from Products.PythonScripts.PythonScript import manage_addPythonScript
618
+
619
+ script1_body = "container.script2()"
620
+ manage_addPythonScript(
621
+ self.folder,
622
+ "script1",
623
+ file=script1_body,
624
+ )
625
+ script2_body = "1 / 0"
626
+ manage_addPythonScript(
627
+ self.folder,
628
+ "script2",
629
+ file=script2_body,
630
+ )
631
+ try:
632
+ self.folder.script1()
633
+ except ZeroDivisionError:
634
+ formatted_exception = self._format_exception()
635
+ self.assertIn(script1_body, formatted_exception)
636
+ self.assertIn(script2_body, formatted_exception)
@@ -16,7 +16,7 @@
16
16
  <div class="form-group row ">
17
17
  <label for="params" class="form-label col-sm-3 col-md-2">Parameter List</label>
18
18
  <div class="col-sm-9 col-md-10">
19
- <input id="params" class="form-control" type="text" name="params" value="&dtml-params;" />
19
+ <input id="params" class="form-control code" type="text" name="params" value="&dtml-params;" />
20
20
  </div>
21
21
  </div>
22
22
  </dtml-with>
@@ -1,11 +1,11 @@
1
- Metadata-Version: 2.1
1
+ Metadata-Version: 2.4
2
2
  Name: Products.PythonScripts
3
- Version: 5.0
3
+ Version: 5.2
4
4
  Summary: Provides support for restricted execution of Python scripts in Zope.
5
5
  Home-page: https://github.com/zopefoundation/Products.PythonScripts
6
6
  Author: Zope Foundation and Contributors
7
7
  Author-email: zope-dev@zope.dev
8
- License: ZPL 2.1
8
+ License: ZPL-2.1
9
9
  Project-URL: Issue Tracker, https://github.com/zopefoundation/Products.PythonScripts/issues
10
10
  Project-URL: Sources, https://github.com/zopefoundation/Products.PythonScripts
11
11
  Classifier: Development Status :: 6 - Mature
@@ -16,22 +16,32 @@ Classifier: License :: OSI Approved :: Zope Public License
16
16
  Classifier: Operating System :: OS Independent
17
17
  Classifier: Programming Language :: Python
18
18
  Classifier: Programming Language :: Python :: 3
19
- Classifier: Programming Language :: Python :: 3.7
20
- Classifier: Programming Language :: Python :: 3.8
21
- Classifier: Programming Language :: Python :: 3.9
22
19
  Classifier: Programming Language :: Python :: 3.10
23
20
  Classifier: Programming Language :: Python :: 3.11
21
+ Classifier: Programming Language :: Python :: 3.12
22
+ Classifier: Programming Language :: Python :: 3.13
23
+ Classifier: Programming Language :: Python :: 3.14
24
24
  Classifier: Programming Language :: Python :: Implementation :: CPython
25
- Requires-Python: >=3.7
25
+ Requires-Python: >=3.10
26
26
  License-File: LICENSE.txt
27
- Requires-Dist: setuptools
28
27
  Requires-Dist: AccessControl
29
28
  Requires-Dist: Acquisition
30
29
  Requires-Dist: DateTime
31
30
  Requires-Dist: DocumentTemplate
32
- Requires-Dist: RestrictedPython (>=4.0b5)
31
+ Requires-Dist: RestrictedPython>=4.0b5
33
32
  Requires-Dist: zExceptions
34
- Requires-Dist: Zope (>=4.1.2)
33
+ Requires-Dist: Zope>=4.1.2
34
+ Dynamic: author
35
+ Dynamic: author-email
36
+ Dynamic: classifier
37
+ Dynamic: description
38
+ Dynamic: home-page
39
+ Dynamic: license
40
+ Dynamic: license-file
41
+ Dynamic: project-url
42
+ Dynamic: requires-dist
43
+ Dynamic: requires-python
44
+ Dynamic: summary
35
45
 
36
46
  .. image:: https://github.com/zopefoundation/Products.PythonScripts/actions/workflows/tests.yml/badge.svg
37
47
  :target: https://github.com/zopefoundation/Products.PythonScripts/actions/workflows/tests.yml
@@ -125,6 +135,29 @@ in the Products directory.
125
135
  Changelog
126
136
  =========
127
137
 
138
+ 5.2 (2025-11-20)
139
+ ----------------
140
+
141
+ - Switch to PEP 420 native namespace.
142
+
143
+ - Add support for Python 3.14.
144
+
145
+ - Drop support for Python 3.8 and 3.9.
146
+
147
+
148
+ 5.1 (2024-10-16)
149
+ ----------------
150
+
151
+ - Fix behavior when uploading no file in ``Zope >= 5.8.1``.
152
+
153
+ - Add support for Python 3.12 and 3.13
154
+
155
+ - Drop support for Python 3.7.
156
+
157
+ - Show Python Scripts source code in tracebacks.
158
+ `#64 <https://github.com/zopefoundation/Products.PythonScripts/issues/64>`_
159
+
160
+
128
161
  5.0 (2023-02-01)
129
162
  ----------------
130
163
 
@@ -1,13 +1,12 @@
1
- Products.PythonScripts-5.0-py3.11-nspkg.pth,sha256=nq2GLEDBKV9G0i4LEhkEEQT0gMqQlG1ToxVfs90lBMs,549
2
- Products/PythonScripts/PythonScript.py,sha256=I9UNQfBjqM0LbNEVtkGzq5W5gZR_qNSsBVjNxw6pMzQ,20540
1
+ Products/PythonScripts/PythonScript.py,sha256=akLe6wdOQjpwy_AsTH9VPIerEBHV4hOaYCTAACrX3TQ,20940
3
2
  Products/PythonScripts/Utility.py,sha256=o_v9f8K9Apmz7nuyO3sx5MszFCg573LEA_HbPepo6zQ,860
4
- Products/PythonScripts/__init__.py,sha256=iJZv-69KQR73xMaN08IUeTR3Xua1Ga9CB1U_k8BE6YM,2210
3
+ Products/PythonScripts/__init__.py,sha256=hPI8a30soz2sNbeuRTHO-Klib_pXD667gihOax2OHKA,2224
5
4
  Products/PythonScripts/configure.zcml,sha256=oEllo95Kz_TbT-o33fkM3K7eJekI9UtYq7VheP3TQ80,222
6
5
  Products/PythonScripts/standard.py,sha256=q7Y9HmwoNeep9Ge2TZPIxOHeSJuum0Gws_Gky0x0s74,3741
7
6
  Products/PythonScripts/Extensions/RemotePS.py,sha256=GThnZEHNoyEYkeYySqEUk2mLZgaUYTYBimIIexc7edA,612
8
7
  Products/PythonScripts/tests/__init__.py,sha256=47DEQpj8HBSa-_TImW-5JCeuQeRkm5NMpJWZG3hSuFU,0
9
8
  Products/PythonScripts/tests/testBindings.py,sha256=nd5yJl0SOpJVguSDgkzZG43j_wzs7yTXR5zf44BhScc,9454
10
- Products/PythonScripts/tests/testPythonScript.py,sha256=I2cTYSKC-u4iFBoiGAX8z3LP9Mqa4tq2MAjwRY4pw_k,21632
9
+ Products/PythonScripts/tests/testPythonScript.py,sha256=He8_FpTehy1wp8kBoXCHkcY4Wpimu6MUaPOThv8HO_4,23102
11
10
  Products/PythonScripts/tests/tscripts/big_boolean.ps,sha256=pnSjggXHNB4p2HAGaoXrYdf9N-tVgISJdZkSxHevcOk,119
12
11
  Products/PythonScripts/tests/tscripts/boolean_map.ps,sha256=Ct15mgZ_j9wquw1QsgPNq1xqUU--PpFK_7MK63GvPLw,296
13
12
  Products/PythonScripts/tests/tscripts/class.__name__.ps,sha256=xYWFpxh8TWqSvJgEgCXBJysUeIIoLi9GOgvy1kG2jns,87
@@ -27,12 +26,11 @@ Products/PythonScripts/tests/tscripts/tuple_unpack_assignment.ps,sha256=ndiygHw2
27
26
  Products/PythonScripts/tests/tscripts/while_loop.ps,sha256=oka5M0x1cdqkn5KAOctBl0hFOoNf-g5eaCE1m13APYA,43
28
27
  Products/PythonScripts/www/default_content,sha256=zW3seGJBcptFXv3jecfcKEslpxCfq4leBSDzJwKtQ8w,465
29
28
  Products/PythonScripts/www/pyScriptAdd.dtml,sha256=d4IwyyX3x7nj1McBEnXPSvcZT9JBdc6OkS6fXdKcq08,1620
30
- Products/PythonScripts/www/pyScriptEdit.dtml,sha256=e4-KQaK-J-MIbFMAI4eXbvCc0YuJ1RvluT5jeT08cL4,3343
29
+ Products/PythonScripts/www/pyScriptEdit.dtml,sha256=sCSnnxJlQmSo2G2464bvLgsmb5gLvWjHfNQw_BRhlMY,3348
31
30
  Products/PythonScripts/www/pyScriptProxy.dtml,sha256=xV-hC9-ZEcGi-OVde2VVMt-2_OTJ1egN-cSwCr93dBQ,1147
32
- Products.PythonScripts-5.0.dist-info/LICENSE.txt,sha256=PmcdsR32h1FswdtbPWXkqjg-rKPCDOo_r1Og9zNdCjw,2070
33
- Products.PythonScripts-5.0.dist-info/METADATA,sha256=DfI_63PWY1cKpvYBNQIxNm8gXpFKMWIzDbVpmPYmfAo,10343
34
- Products.PythonScripts-5.0.dist-info/WHEEL,sha256=2wepM1nk4DS4eFpYrW1TTqPcoGNfHhhO_i5m4cOimbo,92
35
- Products.PythonScripts-5.0.dist-info/entry_points.txt,sha256=bYD9dZ1s4djQ3ga5W8tQz28uFzu1tOOA4knl5p6wNy0,76
36
- Products.PythonScripts-5.0.dist-info/namespace_packages.txt,sha256=UgMr3LVXWHS9vjnxTk8VOZd7xxgBn5T02HOaMRXjjTM,9
37
- Products.PythonScripts-5.0.dist-info/top_level.txt,sha256=UgMr3LVXWHS9vjnxTk8VOZd7xxgBn5T02HOaMRXjjTM,9
38
- Products.PythonScripts-5.0.dist-info/RECORD,,
31
+ products_pythonscripts-5.2.dist-info/licenses/LICENSE.txt,sha256=PmcdsR32h1FswdtbPWXkqjg-rKPCDOo_r1Og9zNdCjw,2070
32
+ products_pythonscripts-5.2.dist-info/METADATA,sha256=Q62fzgLCbZvJGQHljHLw_Jh8uEMKWmuXra1tUiu8nX8,10982
33
+ products_pythonscripts-5.2.dist-info/WHEEL,sha256=_zCd3N1l69ArxyTb8rzEoP9TpbYXkqRFSNOD5OuxnTs,91
34
+ products_pythonscripts-5.2.dist-info/entry_points.txt,sha256=bYD9dZ1s4djQ3ga5W8tQz28uFzu1tOOA4knl5p6wNy0,76
35
+ products_pythonscripts-5.2.dist-info/top_level.txt,sha256=UgMr3LVXWHS9vjnxTk8VOZd7xxgBn5T02HOaMRXjjTM,9
36
+ products_pythonscripts-5.2.dist-info/RECORD,,
@@ -1,5 +1,5 @@
1
1
  Wheel-Version: 1.0
2
- Generator: bdist_wheel (0.38.4)
2
+ Generator: setuptools (80.9.0)
3
3
  Root-Is-Purelib: true
4
4
  Tag: py3-none-any
5
5
 
@@ -1 +0,0 @@
1
- import sys, types, os;has_mfs = sys.version_info > (3, 5);p = os.path.join(sys._getframe(1).f_locals['sitedir'], *('Products',));importlib = has_mfs and __import__('importlib.util');has_mfs and __import__('importlib.machinery');m = has_mfs and sys.modules.setdefault('Products', importlib.util.module_from_spec(importlib.machinery.PathFinder.find_spec('Products', [os.path.dirname(p)])));m = m or sys.modules.setdefault('Products', types.ModuleType('Products'));mp = (m or []) and m.__dict__.setdefault('__path__',[]);(p not in mp) and mp.append(p)