Commit 93e22510 authored by Jérome Perrin's avatar Jérome Perrin

ERP5 py3: WIP ( all changes squashed )

software/slapos-sr-testing: add erp5-py3

---

WIP ERP5: XXX dumps() parameter for longrequest promise 🚧

Not sure why it was OK on python2 and not sure if this has to be done
or the promise code needs to cast the results of getConfig()

---

py3: do not enable NEO test yet 🚧

at this point they all fail after long timeouts, because neo does not
support py3 yet

stack/erp5: version up APacheDEX 2.0 (py3 only)
Co-authored-by: Kazuhiko Shiozaki's avatarKazuhiko SHIOZAKI <kazuhiko@nexedi.com>
Co-authored-by: Arnaud Fontaine's avatarArnaud Fontaine <arnaud.fontaine@nexedi.com>
Co-authored-by: Bryton Lacquement's avatarBryton Lacquement <bryton.lacquement@nexedi.com>
parent a3f7a996
...@@ -57,6 +57,12 @@ egg-versions = ...@@ -57,6 +57,12 @@ egg-versions =
[ZODB5] [ZODB5]
<= _ZODB <= _ZODB
egg-versions =
ZODB = 5.8.1
transaction = 4.0.0
[ZODB5:python2]
<= _ZODB
egg-versions = egg-versions =
ZODB = 5.8.1 ZODB = 5.8.1
transaction = 3.0.1 transaction = 3.0.1
...@@ -94,11 +100,17 @@ setup-eggs = ${python-cffi:egg} ...@@ -94,11 +100,17 @@ setup-eggs = ${python-cffi:egg}
# eggs that are common to ZODB4 and ZODB5. # eggs that are common to ZODB4 and ZODB5.
[versions] [versions]
BTrees = 4.11.3 BTrees = 5.1.0
persistent = 4.9.3 persistent = 5.1.0
zodbpickle = 2.6.0 zodbpickle = 3.1.0
# Provide ZODB3 for those eggs that still care about ZODB3 compatibility - # Provide ZODB3 for those eggs that still care about ZODB3 compatibility -
# for example wendelin.core. ZODB3 3.11 is just a dependency egg on _latest_ # for example wendelin.core. ZODB3 3.11 is just a dependency egg on _latest_
# ZODB, persistent, BTrees and ZEO. # ZODB, persistent, BTrees and ZEO.
ZODB3 = 3.11.0 ZODB3 = 3.11.0
[versions:python2]
BTrees = 4.11.3
persistent = 4.9.3
zodbpickle = 2.6.0
From 3666a7afd46ea6d069606450c520b8b7e2b5fddf Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Thu, 22 Feb 2024 23:33:41 +0900
Subject: [PATCH] Make dict views behave like their unrestricted versions
unlike the restricted versions, the unrestricted versions:
- are not iterators, they are views
- have a len
- are false when the mapping is empty, true otherwise
- are instances of collections.abc.MappingView
During this refactoring, also change `.items()` to validate
ach keys and values, like `.keys()` and `.values()` do.
---
CHANGES.rst | 7 ++++
src/AccessControl/ZopeGuards.py | 50 ++++++++++++++++++-----
src/AccessControl/tests/actual_python.py | 33 +++++++++++++++
src/AccessControl/tests/testZopeGuards.py | 34 +++++++++++----
4 files changed, 104 insertions(+), 20 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index f35a8d2..073b791 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -8,6 +8,13 @@ For changes before version 3.0, see ``HISTORY.rst``.
- Nothing changed yet.
+- Make dict views (`.keys()`, `.items()` and `.values()`) behave like their
+ unrestricted versions.
+ (`#147 <https://github.com/zopefoundation/AccessControl/pull/147>`_)
+
+- Make `.items()` validate each keys and values, like `.keys()` and
+ `.values()` do.
+
6.3 (2023-11-20)
----------------
diff --git a/src/AccessControl/ZopeGuards.py b/src/AccessControl/ZopeGuards.py
index 84c2e9e..bc24941 100644
--- a/src/AccessControl/ZopeGuards.py
+++ b/src/AccessControl/ZopeGuards.py
@@ -12,6 +12,7 @@
##############################################################################
+import collections.abc
import math
import random
import string
@@ -127,13 +128,18 @@ def guarded_pop(key, default=_marker):
return guarded_pop
-def get_iter(c, name):
- iter = getattr(c, name)
+def get_mapping_view(c, name):
- def guarded_iter():
- return SafeIter(iter(), c)
+ view_class = {
+ 'keys': SafeKeysView,
+ 'items': SafeItemsView,
+ 'values': SafeValuesView,
+ }
- return guarded_iter
+ def guarded_mapping_view():
+ return view_class[name](c)
+
+ return guarded_mapping_view
def get_list_pop(lst, name):
@@ -153,18 +159,15 @@ def guarded_pop(index=-1):
'copy': 1,
'fromkeys': 1,
'get': get_dict_get,
- 'items': 1,
+ 'items': get_mapping_view,
+ 'keys': get_mapping_view,
'pop': get_dict_pop,
'popitem': 1,
'setdefault': 1,
'update': 1,
+ 'values': get_mapping_view,
}
-_dict_white_list.update({
- 'keys': get_iter,
- 'values': get_iter,
-})
-
def _check_dict_access(name, value):
# Check whether value is a dict method
@@ -262,6 +265,31 @@ def __next__(self):
next = __next__
+class _SafeMappingView:
+ __allow_access_to_unprotected_subobjects__ = 1
+
+ def __iter__(self):
+ for e in super().__iter__():
+ guard(self._mapping, e)
+ yield e
+
+
+class SafeKeysView(_SafeMappingView, collections.abc.KeysView):
+ pass
+
+
+class SafeValuesView(_SafeMappingView, collections.abc.ValuesView):
+ pass
+
+
+class SafeItemsView(_SafeMappingView, collections.abc.ItemsView):
+ def __iter__(self):
+ for k, v in super().__iter__():
+ guard(self._mapping, k)
+ guard(self._mapping, v)
+ yield k, v
+
+
class NullIter(SafeIter):
def __init__(self, ob):
self._iter = ob
diff --git a/src/AccessControl/tests/actual_python.py b/src/AccessControl/tests/actual_python.py
index 3405b8e..866a480 100644
--- a/src/AccessControl/tests/actual_python.py
+++ b/src/AccessControl/tests/actual_python.py
@@ -123,6 +123,39 @@ def f7():
access = getattr(d, meth)
result = sorted(access())
assert result == expected[kind], (meth, kind, result, expected[kind])
+ assert len(access()) == len(expected[kind]), (meth, kind, "len")
+ iter_ = access() # iterate twice on the same view
+ assert list(iter_) == list(iter_)
+
+ assert sorted([k for k in getattr(d, meth)()]) == expected[kind]
+ assert sorted(k for k in getattr(d, meth)()) == expected[kind]
+ assert {k: v for k, v in d.items()} == d
+
+ assert 1 in d
+ assert 1 in d.keys()
+ assert 2 in d.values()
+ assert (1, 2) in d.items()
+
+ assert d
+ assert d.keys()
+ assert d.values()
+ assert d.items()
+
+ empty_d = {}
+ assert not empty_d
+ assert not empty_d.keys()
+ assert not empty_d.values()
+ assert not empty_d.items()
+
+ smaller_d = {1: 2}
+ for m, _ in methods:
+ assert getattr(d, m)() != getattr(smaller_d, m)()
+ assert not getattr(d, m)() == getattr(smaller_d, m)()
+ if m != 'values':
+ assert getattr(d, m)() > getattr(smaller_d, m)()
+ assert getattr(d, m)() >= getattr(smaller_d, m)()
+ assert getattr(smaller_d, m)() < getattr(d, m)()
+ assert getattr(smaller_d, m)() <= getattr(d, m)()
f7()
diff --git a/src/AccessControl/tests/testZopeGuards.py b/src/AccessControl/tests/testZopeGuards.py
index 533bfa2..50eeca9 100644
--- a/src/AccessControl/tests/testZopeGuards.py
+++ b/src/AccessControl/tests/testZopeGuards.py
@@ -258,23 +258,40 @@ def test_pop_validates(self):
self.assertTrue(sm.calls)
def test_keys_empty(self):
- from AccessControl.ZopeGuards import get_iter
- keys = get_iter({}, 'keys')
+ from AccessControl.ZopeGuards import get_mapping_view
+ keys = get_mapping_view({}, 'keys')
self.assertEqual(list(keys()), [])
+ def test_kvi_len(self):
+ from AccessControl.ZopeGuards import get_mapping_view
+ for attr in ("keys", "values", "items"):
+ with self.subTest(attr):
+ view = get_mapping_view({'a': 1}, attr)
+ self.assertEqual(len(view()), 1)
+
def test_keys_validates(self):
sm = SecurityManager()
old = self.setSecurityManager(sm)
keys = guarded_getattr({GuardTestCase: 1}, 'keys')
try:
- next(keys())
+ next(iter(keys()))
finally:
self.setSecurityManager(old)
self.assertTrue(sm.calls)
+ def test_items_validates(self):
+ sm = SecurityManager()
+ old = self.setSecurityManager(sm)
+ items = guarded_getattr({GuardTestCase: GuardTestCase}, 'items')
+ try:
+ next(iter(items()))
+ finally:
+ self.setSecurityManager(old)
+ self.assertEqual(len(sm.calls), 2)
+
def test_values_empty(self):
- from AccessControl.ZopeGuards import get_iter
- values = get_iter({}, 'values')
+ from AccessControl.ZopeGuards import get_mapping_view
+ values = get_mapping_view({}, 'values')
self.assertEqual(list(values()), [])
def test_values_validates(self):
@@ -282,18 +299,17 @@ def test_values_validates(self):
old = self.setSecurityManager(sm)
values = guarded_getattr({GuardTestCase: 1}, 'values')
try:
- next(values())
+ next(iter(values()))
finally:
self.setSecurityManager(old)
self.assertTrue(sm.calls)
def test_kvi_iteration(self):
- from AccessControl.ZopeGuards import SafeIter
d = dict(a=1, b=2)
for attr in ("keys", "values", "items"):
v = getattr(d, attr)()
- si = SafeIter(v)
- self.assertEqual(next(si), next(iter(v)))
+ si = guarded_getattr(d, attr)()
+ self.assertEqual(next(iter(si)), next(iter(v)))
class TestListGuards(GuardTestCase):
From 77f86b50f097dcf364e0d140e45593bf001d46bc Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 1 Mar 2024 09:49:17 +0900
Subject: [PATCH] set metadata in setup.py for compatibility with old slapos
buildout
---
setup.py | 5 +++++
1 file changed, 5 insertions(+)
diff --git a/setup.py b/setup.py
index 1bf0bcff5..a93fe7b22 100755
--- a/setup.py
+++ b/setup.py
@@ -987,6 +987,11 @@ ext_modules = [
try:
setup(
+ name='pillow',
+ version='10.2.0',
+ packages=["PIL"],
+ include_package_data=True,
+ package_dir={"": "src"},
cmdclass={"build_ext": pil_build_ext},
ext_modules=ext_modules,
zip_safe=not (debug_build() or PLATFORM_MINGW),
--
2.42.0
From 3c6b815bbb2a9300984a7b50cb5ec5375bf4588e Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Tue, 2 Apr 2024 21:54:07 +0900
Subject: [PATCH] Revive TRIGGER_WORKFLOW_METHOD support, ERP5 uses it
---
src/Products/DCWorkflow/DCWorkflow.py | 47 +++++++++++++++++++
src/Products/DCWorkflow/Transitions.py | 1 +
.../dtml/transition_properties.dtml | 10 ++++
src/Products/DCWorkflow/dtml/transitions.dtml | 3 +-
src/Products/DCWorkflow/exportimport.py | 2 +-
5 files changed, 61 insertions(+), 2 deletions(-)
diff --git a/src/Products/DCWorkflow/DCWorkflow.py b/src/Products/DCWorkflow/DCWorkflow.py
index 9adf05c..d0306dc 100644
--- a/src/Products/DCWorkflow/DCWorkflow.py
+++ b/src/Products/DCWorkflow/DCWorkflow.py
@@ -38,6 +38,7 @@ from .Expression import createExprContext
from .interfaces import IDCWorkflowDefinition
from .Transitions import TRIGGER_AUTOMATIC
from .Transitions import TRIGGER_USER_ACTION
+from .Transitions import TRIGGER_WORKFLOW_METHOD
from .utils import Message as _
from .utils import modifyRolesForGroup
from .utils import modifyRolesForPermission
@@ -278,6 +279,52 @@ class DCWorkflowDefinition(WorkflowUIMixin, Folder):
raise Unauthorized(action)
self._changeStateOf(ob, tdef, kw)
+ @security.private
+ def isWorkflowMethodSupported(self, ob, method_id):
+ '''
+ Returns a true value if the given workflow method
+ is supported in the current state.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ return 0
+ if method_id in sdef.transitions:
+ tdef = self.transitions.get(method_id, None)
+ if (tdef is not None and
+ tdef.trigger_type == TRIGGER_WORKFLOW_METHOD and
+ self._checkTransitionGuard(tdef, ob)):
+ return 1
+ return 0
+
+ @security.private
+ def wrapWorkflowMethod(self, ob, method_id, func, args, kw):
+ '''
+ Allows the user to request a workflow action. This method
+ must perform its own security checks.
+ '''
+ sdef = self._getWorkflowStateOf(ob)
+ if sdef is None:
+ raise WorkflowException('Object is in an undefined state')
+ if method_id not in sdef.transitions:
+ raise Unauthorized(method_id)
+ tdef = self.transitions.get(method_id, None)
+ if tdef is None or tdef.trigger_type != TRIGGER_WORKFLOW_METHOD:
+ raise WorkflowException(
+ 'Transition %s is not triggered by a workflow method'
+ % method_id)
+ if not self._checkTransitionGuard(tdef, ob):
+ raise Unauthorized(method_id)
+ res = func(*args, **kw)
+ try:
+ self._changeStateOf(ob, tdef)
+ except ObjectDeleted:
+ # Re-raise with a different result.
+ raise ObjectDeleted(res)
+ except ObjectMoved as ex:
+ # Re-raise with a different result.
+ raise ObjectMoved(ex.getNewObject(), res)
+ return res
+
@security.private
def isInfoSupported(self, ob, name):
'''
diff --git a/src/Products/DCWorkflow/Transitions.py b/src/Products/DCWorkflow/Transitions.py
index a6e1e6f..b4e012c 100644
--- a/src/Products/DCWorkflow/Transitions.py
+++ b/src/Products/DCWorkflow/Transitions.py
@@ -31,6 +31,7 @@ from .utils import _dtmldir
TRIGGER_AUTOMATIC = 0
TRIGGER_USER_ACTION = 1
+TRIGGER_WORKFLOW_METHOD = 2
class TransitionDefinition(SimpleItem):
diff --git a/src/Products/DCWorkflow/dtml/transition_properties.dtml b/src/Products/DCWorkflow/dtml/transition_properties.dtml
index d6b8a74..6a0803e 100644
--- a/src/Products/DCWorkflow/dtml/transition_properties.dtml
+++ b/src/Products/DCWorkflow/dtml/transition_properties.dtml
@@ -55,6 +55,16 @@ Initiated by user action
</td>
</tr>
+<tr>
+<th></th>
+<td>
+<dtml-let checked="trigger_type==2 and 'checked' or ' '">
+<input type="radio" name="trigger_type" value="2" &dtml-checked; />
+Initiated by WorkflowMethod
+</dtml-let>
+</td>
+</tr>
+
<tr>
<th align="left">Script (before)</th>
<td>
diff --git a/src/Products/DCWorkflow/dtml/transitions.dtml b/src/Products/DCWorkflow/dtml/transitions.dtml
index 4cdd3d3..37e949c 100644
--- a/src/Products/DCWorkflow/dtml/transitions.dtml
+++ b/src/Products/DCWorkflow/dtml/transitions.dtml
@@ -17,7 +17,8 @@
<td>
Destination state: <code><dtml-if new_state_id>&dtml-new_state_id;<dtml-else>(Remain in state)</dtml-if></code> <br />
Trigger: <dtml-var expr="(trigger_type == 0 and 'Automatic') or
- (trigger_type == 1 and 'User action')">
+ (trigger_type == 1 and 'User action') or
+ (trigger_type == 2 and 'WorkflowMethod')">
<br />
<dtml-if script_name>
Script (before): &dtml-script_name;
diff --git a/src/Products/DCWorkflow/exportimport.py b/src/Products/DCWorkflow/exportimport.py
index f17264d..2374b6e 100644
--- a/src/Products/DCWorkflow/exportimport.py
+++ b/src/Products/DCWorkflow/exportimport.py
@@ -37,7 +37,7 @@ from .interfaces import IDCWorkflowDefinition
from .utils import _xmldir
-TRIGGER_TYPES = ('AUTOMATIC', 'USER')
+TRIGGER_TYPES = ('AUTOMATIC', 'USER', 'METHOD' )
_FILENAME = 'workflows.xml'
--
2.42.0
From 21a91db138cca3ada0e4dff475b061066362410c Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Sat, 17 Feb 2024 23:25:43 +0900
Subject: [PATCH] backport changes from 0.52.29
We can not use 0.52.29 directly because it does not have a setup.py
and our buildout / setuptools tooling is too old.
---
src/SOAPpy/Client.py | 3 ++-
src/SOAPpy/Types.py | 2 ++
2 files changed, 4 insertions(+), 1 deletion(-)
diff --git a/src/SOAPpy/Client.py b/src/SOAPpy/Client.py
index e86c5ec..d2bbefb 100644
--- a/src/SOAPpy/Client.py
+++ b/src/SOAPpy/Client.py
@@ -45,6 +45,7 @@
ident = '$Id: Client.py 1496 2010-03-04 23:46:17Z pooryorick $'
from .version import __version__
+from io import StringIO
#import xml.sax
import urllib.request, urllib.parse, urllib.error
@@ -152,7 +153,7 @@ class HTTP:
return -1, e.line, None
self.headers = response.msg
- self.file = response.fp
+ self.file = StringIO(response.fp.read().decode('utf-8'))
return response.status, response.reason, response.msg
def close(self):
diff --git a/src/SOAPpy/Types.py b/src/SOAPpy/Types.py
index de9dcac..cf08d17 100644
--- a/src/SOAPpy/Types.py
+++ b/src/SOAPpy/Types.py
@@ -1451,6 +1451,8 @@ class arrayType(collections.UserList, compoundType):
def __getitem__(self, item):
try:
return self.data[int(item)]
+ except TypeError:
+ return self.data[item]
except ValueError:
return getattr(self, item)
--
2.42.0
From c8c52a14d481403f1db252631d3cc4b8e86e1798 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <perrinjerome@gmail.com>
Date: Sat, 17 Feb 2024 05:32:46 +0000
Subject: [PATCH] Fix authentication error viewing ZMI with a user defined
outside of zope root.
Fixes https://github.com/zopefoundation/Zope/issues/1195
5.9 (2023-11-24)
----------------
diff --git a/src/App/dtml/manage_page_header.dtml b/src/App/dtml/manage_page_header.dtml
index 2dcb047552..e8b8659e10 100644
--- a/src/App/dtml/manage_page_header.dtml
+++ b/src/App/dtml/manage_page_header.dtml
@@ -10,24 +10,27 @@
</dtml-let>
<title><dtml-if title_or_id><dtml-var title_or_id><dtml-else>Zope</dtml-if></title>
+<dtml-let basepath="'/'.join([''] + [p for p in (REQUEST['BASEPATH1'], REQUEST.get('AUTHENTICATION_PATH')) if p])">
+
<dtml-in css_urls>
- <link rel="stylesheet" type="text/css" href="&dtml-BASEPATH1;&dtml-sequence-item;" />
+ <link rel="stylesheet" type="text/css" href="&dtml-basepath;&dtml-sequence-item;" />
</dtml-in>
<dtml-in js_urls>
- <script src="&dtml-BASEPATH1;&dtml-sequence-item;"></script>
+ <script src="&dtml-basepath;&dtml-sequence-item;"></script>
</dtml-in>
-<link rel="shortcut icon" type="image/x-icon" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon.ico" />
-<link rel="apple-touch-icon" sizes="180x180" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/apple-touch-icon.png" />
-<link rel="icon" type="image/png" sizes="32x32" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon-32x32.png" />
-<link rel="icon" type="image/png" sizes="16x16" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/favicon-16x16.png" />
-<link rel="manifest" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/site.webmanifest" />
-<link rel="mask-icon" href="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/safari-pinned-tab.svg" color="#5bbad5" />
-<meta name="msapplication-config" content="&dtml-BASEPATH1;/++resource++zmi/logo/favicon/browserconfig.xml"/>
+<link rel="shortcut icon" type="image/x-icon" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon.ico" />
+<link rel="apple-touch-icon" sizes="180x180" href="&dtml-basepath;/++resource++zmi/logo/favicon/apple-touch-icon.png" />
+<link rel="icon" type="image/png" sizes="32x32" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon-32x32.png" />
+<link rel="icon" type="image/png" sizes="16x16" href="&dtml-basepath;/++resource++zmi/logo/favicon/favicon-16x16.png" />
+<link rel="manifest" href="&dtml-basepath;/++resource++zmi/logo/favicon/site.webmanifest" />
+<link rel="mask-icon" href="&dtml-basepath;/++resource++zmi/logo/favicon/safari-pinned-tab.svg" color="#5bbad5" />
+<meta name="msapplication-config" content="&dtml-basepath;/++resource++zmi/logo/favicon/browserconfig.xml"/>
<meta name="msapplication-TileColor" content="#2d89ef" />
<meta name="theme-color" content="#ffffff" />
</head>
+</dtml-let>
<!-- REFACT what is a better way to get the last part of the current URL? -->
<body id="nodeid-<dtml-var "getId()">" class="zmi zmi-<dtml-var "this().meta_type.replace(' ', '-').replace('(', '').replace(')', '')"> zmi-<dtml-var "URL0[_.len(URL1)+1:]">">
</dtml-unless>
diff --git a/src/zmi/styles/tests.py b/src/zmi/styles/tests.py
index 256edfa74b..36c7b65bec 100644
--- a/src/zmi/styles/tests.py
+++ b/src/zmi/styles/tests.py
@@ -21,6 +21,8 @@ def setupZCML():
class SubscriberTests(Testing.ZopeTestCase.FunctionalTestCase):
"""Testing .subscriber.*"""
+ base_path = f'/{Testing.ZopeTestCase.folder_name}'
+
def call_manage_main(self):
"""Call /folder/manage_main and return the HTML text."""
def _call_manage_main(self):
@@ -29,7 +31,7 @@ def _call_manage_main(self):
# which the WSGI publisher does not expect.
endInteraction()
response = self.publish(
- f'/{Testing.ZopeTestCase.folder_name}/manage_main',
+ f'{self.base_path}/manage_main',
basic=basic_auth)
return str(response)
return temporaryPlacelessSetUp(
@@ -40,11 +42,11 @@ def test_subscriber__css_paths__1(self):
from .subscriber import css_paths
body = self.call_manage_main()
for path in css_paths(None):
- self.assertIn(path, body)
+ self.assertIn(f'href="{self.base_path}{path}"', body)
def test_subscriber__js_paths__1(self):
"""The paths it returns are rendered in the ZMI."""
from .subscriber import js_paths
body = self.call_manage_main()
for path in js_paths(None):
- self.assertIn(path, body)
+ self.assertIn(f'src="{self.base_path}{path}"', body)
From 7f4c61c0d7fe0751be93e80683659271fa0c65a3 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <perrinjerome@gmail.com>
Date: Sat, 17 Feb 2024 13:06:28 +0000
Subject: [PATCH] Fix Content-Disposition heeader for clients without rfc6266
support.
Since https://github.com/zopefoundation/Zope/pull/893 the
Content-Disposition header supports non-ascii filenames, by containing
the filename in ascii and the filename in UTF-8, but the ascii version
was produced by applying `str` on a `bytes` instance, so it looks like
`b'file.txt'` instead of `file.txt`.
---
CHANGES.rst | 3 +++
src/ZPublisher/HTTPResponse.py | 3 ++-
src/ZPublisher/tests/testHTTPResponse.py | 4 ++--
3 files changed, 7 insertions(+), 3 deletions(-)
diff --git a/CHANGES.rst b/CHANGES.rst
index e8dedc6a66..30061e2093 100644
--- a/CHANGES.rst
+++ b/CHANGES.rst
@@ -18,6 +18,9 @@ https://github.com/zopefoundation/Zope/blob/4.x/CHANGES.rst
- Fix redirections to URLs with host given as IP-literal with brackets.
Fixes `#1191 <https://github.com/zopefoundation/Zope/issues/1191>`_.
+- Fix ``Content-Disposition`` filename for clients without rfc6266 support.
+ (`#1198 <https://github.com/zopefoundation/Zope/pull/1198>`_)
+
5.9 (2023-11-24)
----------------
diff --git a/src/ZPublisher/HTTPResponse.py b/src/ZPublisher/HTTPResponse.py
index ed6bc00a89..264a488a03 100644
--- a/src/ZPublisher/HTTPResponse.py
+++ b/src/ZPublisher/HTTPResponse.py
@@ -142,7 +142,8 @@ def make_content_disposition(disposition, file_name):
#
# a special header has to be crafted
# also see https://tools.ietf.org/html/rfc6266#appendix-D
- encoded_file_name = file_name.encode('us-ascii', errors='ignore')
+ encoded_file_name = file_name.encode(
+ 'us-ascii', errors='ignore').decode()
header += f'; filename="{encoded_file_name}"'
quoted_file_name = quote(file_name)
header += f'; filename*=UTF-8\'\'{quoted_file_name}'
diff --git a/src/ZPublisher/tests/testHTTPResponse.py b/src/ZPublisher/tests/testHTTPResponse.py
index 1613b0ea59..5d64b5b3dd 100644
--- a/src/ZPublisher/tests/testHTTPResponse.py
+++ b/src/ZPublisher/tests/testHTTPResponse.py
@@ -1433,7 +1433,7 @@ def test_ascii(self):
def test_latin_one(self):
self.assertEqual(
make_content_disposition('inline', 'Dänemark.png'),
- 'inline; filename="b\'Dnemark.png\'"; filename*=UTF-8\'\'D%C3%A4nemark.png' # noqa: E501
+ 'inline; filename="Dnemark.png"; filename*=UTF-8\'\'D%C3%A4nemark.png' # noqa: E501
)
def test_unicode(self):
@@ -1445,7 +1445,7 @@ def test_unicode(self):
"""
self.assertEqual(
make_content_disposition('inline', 'ıq.png'),
- 'inline; filename="b\'q.png\'"; filename*=UTF-8\'\'%C4%B1q.png'
+ 'inline; filename="q.png"; filename*=UTF-8\'\'%C4%B1q.png'
)
From c56146829ab065183c709229a9daa682cc445212 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?J=C3=A9rome=20Perrin?= <jerome@nexedi.com>
Date: Fri, 26 Apr 2024 15:09:39 +0900
Subject: [PATCH] fix loading font for ean13
use same technique as for code128
---
hubarcode/ean13/renderer.py | 6 ++++--
1 file changed, 4 insertions(+), 2 deletions(-)
diff --git a/hubarcode/ean13/renderer.py b/hubarcode/ean13/renderer.py
index 654501e..ff5f518 100644
--- a/hubarcode/ean13/renderer.py
+++ b/hubarcode/ean13/renderer.py
@@ -78,8 +78,10 @@ class EAN13Renderer:
# Draw the text
font_size = font_sizes.get(bar_width, 24)
- # Use relative name, PIL will do searching for us
- fontfile = os.path.join("fonts", "courR%02d.pil" % font_size)
+ # Locate and load the font file relative to the module
+ ean13dir, _ = os.path.split(__file__)
+ rootdir, _ = os.path.split(ean13dir)
+ fontfile = os.path.join(rootdir, "fonts", "courR%02d.pil" % font_size)
font = ImageFont.load_path(fontfile)
draw = ImageDraw.Draw(img)
--
2.42.0
This diff is collapsed.
...@@ -10,11 +10,12 @@ parts = ...@@ -10,11 +10,12 @@ parts =
LDFLAGS = -L${hdf5:location}/lib -Wl,-rpath=${hdf5:location}/lib LDFLAGS = -L${hdf5:location}/lib -Wl,-rpath=${hdf5:location}/lib
CPPFLAGS = -I${hdf5:location}/include CPPFLAGS = -I${hdf5:location}/include
LD_LIBRARY_PATH=${hdf5:location}/lib LD_LIBRARY_PATH=${hdf5:location}/lib
HDF5_DIR=${hdf5:location}
[h5py] [h5py]
recipe = zc.recipe.egg:custom recipe = zc.recipe.egg:custom
egg = h5py egg = h5py
setup-eggs = setup-eggs =
${cython:egg} ${cython:egg}
${numpy:egg} ${numpy:egg}
pkgconfig pkgconfig
......
...@@ -11,7 +11,7 @@ parts = ...@@ -11,7 +11,7 @@ parts =
<= numpy-env <= numpy-env
[ipython] [ipython]
recipe = zc.recipe.egg:custom recipe = zc.recipe.egg
egg = ipython egg = ipython
environment = ipython-env environment = ipython-env
setup-eggs = setup-eggs =
......
...@@ -75,7 +75,7 @@ class ERP5Kernel(Kernel): ...@@ -75,7 +75,7 @@ class ERP5Kernel(Kernel):
self.title = None self.title = None
# Allowed HTTP request code list for making request to erp5 from Kernel # Allowed HTTP request code list for making request to erp5 from Kernel
# This list should be to used check status_code before making requests to erp5 # This list should be to used check status_code before making requests to erp5
self.allowed_HTTP_request_code_list = range(500, 511) self.allowed_HTTP_request_code_list = list(range(500, 511))
# Append request code 200 in the allowed HTTP status code list # Append request code 200 in the allowed HTTP status code list
self.allowed_HTTP_request_code_list.append(200) self.allowed_HTTP_request_code_list.append(200)
......
...@@ -14,9 +14,8 @@ parts += ...@@ -14,9 +14,8 @@ parts +=
# Always build GCC for Fortran (see openblas). # Always build GCC for Fortran (see openblas).
max_version = 0 max_version = 0
[jupyter] [jupyter:python2]
extra-eggs = extra-eggs =
python_executable = ${buildout:bin-directory}/${:interpreter}
[download-file-base] [download-file-base]
recipe = slapos.recipe.build:download recipe = slapos.recipe.build:download
...@@ -46,7 +45,7 @@ context = ...@@ -46,7 +45,7 @@ context =
key develop_eggs_directory buildout:develop-eggs-directory key develop_eggs_directory buildout:develop-eggs-directory
key eggs_directory buildout:eggs-directory key eggs_directory buildout:eggs-directory
key openssl_output openssl-output:openssl key openssl_output openssl-output:openssl
key python_executable jupyter:python_executable key python_executable jupyter:python-executable
key jupyter_config_location jupyter-notebook-config:location key jupyter_config_location jupyter-notebook-config:location
key jupyter_config_filename jupyter-notebook-config:filename key jupyter_config_filename jupyter-notebook-config:filename
key jupyter_set_password_location jupyter-set-password:location key jupyter_set_password_location jupyter-set-password:location
...@@ -59,7 +58,7 @@ context = ...@@ -59,7 +58,7 @@ context =
key custom_js_filename custom-js:filename key custom_js_filename custom-js:filename
key monitor_template_rendered buildout:directory key monitor_template_rendered buildout:directory
[versions] [versions:python2]
Pygments = 2.2.0 Pygments = 2.2.0
ipykernel = 4.5.2 ipykernel = 4.5.2
ipython = 5.3.0 ipython = 5.3.0
......
...@@ -15,11 +15,11 @@ ...@@ -15,11 +15,11 @@
[instance-jupyter-notebook] [instance-jupyter-notebook]
filename = instance.cfg.in filename = instance.cfg.in
md5sum = fd7ed44da8d8723983b8666df2971a36 md5sum = c335782940a8f3b1ff7d4280aeec336e
[jupyter-notebook-config] [jupyter-notebook-config]
filename = jupyter_notebook_config.py.jinja filename = jupyter_notebook_config.py.jinja
md5sum = 9d579353b579b6e488ae6330c7f4ad68 md5sum = a68c96dc2c19f915bccb609c29ffd6c4
[jupyter-set-password] [jupyter-set-password]
filename = jupyter_set_password.cgi.jinja filename = jupyter_set_password.cgi.jinja
...@@ -27,7 +27,7 @@ md5sum = ac10fbcf790bd8e58750cfdd069812d2 ...@@ -27,7 +27,7 @@ md5sum = ac10fbcf790bd8e58750cfdd069812d2
[erp5-kernel] [erp5-kernel]
filename = ERP5kernel.py filename = ERP5kernel.py
md5sum = 7d5309fe79afbcb455c0d8181b42e56c md5sum = da04b99b70b2e327c9e9b4cdd056098e
[kernel-json] [kernel-json]
filename = kernel.json.jinja filename = kernel.json.jinja
......
...@@ -55,7 +55,7 @@ key_file = ${directory:etc}/jupyter_cert.key ...@@ -55,7 +55,7 @@ key_file = ${directory:etc}/jupyter_cert.key
[instance] [instance]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = command-line =
{{ bin_directory }}/jupyter-lab {{ bin_directory }}/jupyter-notebook
--no-browser --no-browser
--ip=${instance-parameter:host} --ip=${instance-parameter:host}
--port=${instance-parameter:port} --port=${instance-parameter:port}
...@@ -69,15 +69,19 @@ environment = ...@@ -69,15 +69,19 @@ environment =
JUPYTER_PATH=${directory:jupyter_dir} JUPYTER_PATH=${directory:jupyter_dir}
JUPYTER_CONFIG_DIR=${directory:jupyter_config_dir} JUPYTER_CONFIG_DIR=${directory:jupyter_config_dir}
JUPYTER_RUNTIME_DIR=${directory:jupyter_runtime_dir} JUPYTER_RUNTIME_DIR=${directory:jupyter_runtime_dir}
JUPYTERLAB_DIR=${directory:jupyterlab-dir}
LANG=C.UTF-8 LANG=C.UTF-8
[jupyter-notebook-config] [jupyter-notebook-config]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
url = {{ jupyter_config_location }}/{{ jupyter_config_filename }} url = {{ jupyter_config_location }}/{{ jupyter_config_filename }}
output = ${directory:jupyter_config_dir}/jupyter_notebook_config.py output = ${directory:jupyter_config_dir}/jupyter_server_config.py
context = context =
raw config_cfg ${buildout:directory}/knowledge0.cfg raw config_cfg ${buildout:directory}/knowledge0.cfg
[jupyter-notebook-config:python2]
output = ${directory:jupyter_config_dir}/jupyter_notebook_config.py
[directory] [directory]
recipe = slapos.cookbook:mkdirectory recipe = slapos.cookbook:mkdirectory
home = ${buildout:directory} home = ${buildout:directory}
...@@ -95,12 +99,13 @@ jupyter_runtime_dir = ${:jupyter_dir}/runtime ...@@ -95,12 +99,13 @@ jupyter_runtime_dir = ${:jupyter_dir}/runtime
jupyter_custom_dir = ${:jupyter_config_dir}/custom jupyter_custom_dir = ${:jupyter_config_dir}/custom
jupyter_nbextensions_dir = ${:jupyter_dir}/nbextensions jupyter_nbextensions_dir = ${:jupyter_dir}/nbextensions
erp5_kernel_dir = ${:jupyter_kernel_dir}/ERP5 erp5_kernel_dir = ${:jupyter_kernel_dir}/ERP5
jupyterlab-dir = ${:jupyter_dir}/lab
[jupyter_notebook] [jupyter_notebook]
# This part is called like this because knowledge0.write uses the part name for # This part is called like this because knowledge0.write uses the part name for
# the section name in the config file. # the section name in the config file.
recipe = slapos.cookbook:zero-knowledge.write recipe = slapos.cookbook:zero-knowledge.write
password = password =
filename = knowledge0.cfg filename = knowledge0.cfg
[read-knowledge0] [read-knowledge0]
...@@ -136,7 +141,7 @@ output = ${directory:erp5_kernel_dir}/kernel.json ...@@ -136,7 +141,7 @@ output = ${directory:erp5_kernel_dir}/kernel.json
context = context =
raw python_executable {{ python_executable }} raw python_executable {{ python_executable }}
raw kernel_dir ${erp5-kernel:target-directory}/{{ erp5_kernel_filename }} raw kernel_dir ${erp5-kernel:target-directory}/{{ erp5_kernel_filename }}
key erp5_url slapconfiguration:configuration.erp5-url key erp5_url slapconfiguration:configuration.erp5-url
raw display_name ERP5 raw display_name ERP5
raw language_name python raw language_name python
......
...@@ -2,28 +2,36 @@ ...@@ -2,28 +2,36 @@
This script initializes Jupyter's configuration such as passwords and other This script initializes Jupyter's configuration such as passwords and other
things. It is run by IPython hence why it can use functions like get_config(). things. It is run by IPython hence why it can use functions like get_config().
''' '''
import ConfigParser
import random
from notebook.auth import passwd
import os import os
import ssl import ssl
import sys
import six
from six.moves.configparser import ConfigParser
if six.PY3:
from jupyter_server.auth import passwd
import secrets
random_password = secrets.token_hex
else:
from notebook.auth import passwd
import random
def random_password(length=10):
result = ""
for i in range(0, length):
result = result + chr(random.randint(0, 25) + ord('a'))
return result
def random_password(length = 10):
result = ""
for i in range(0, length):
result = result + chr(random.randint(0, 25) + ord('a'))
return result
knowledge_0 = '{{ config_cfg }}' knowledge_0 = '{{ config_cfg }}'
if not os.path.exists(knowledge_0): if not os.path.exists(knowledge_0):
print "Your software does <b>not</b> embed 0-knowledge. \ print ("Your software does <b>not</b> embed 0-knowledge.\n"
This interface is useless in this case</body></html>" "This interface is useless in this case</body></html>")
exit(0) exit(0)
c = get_config() c = get_config()
parser = ConfigParser.ConfigParser() parser = ConfigParser()
parser.read(knowledge_0) parser.read(knowledge_0)
if not parser.has_section("jupyter_notebook"): if not parser.has_section("jupyter_notebook"):
...@@ -33,10 +41,49 @@ if not parser.has_option("jupyter_notebook", "password") or \ ...@@ -33,10 +41,49 @@ if not parser.has_option("jupyter_notebook", "password") or \
parser.get("jupyter_notebook", "password") == "": parser.get("jupyter_notebook", "password") == "":
parser.set("jupyter_notebook", "password", random_password()) parser.set("jupyter_notebook", "password", random_password())
c.NotebookApp.password = passwd(parser.get("jupyter_notebook", "password")) c.ServerApp.password = passwd(parser.get("jupyter_notebook", "password"))
c.NotebookApp.ssl_options = {
'ssl_version': ssl.PROTOCOL_TLSv1_2, if six.PY3:
} import pathlib
import jupyterlab
jupyterlab_dir = pathlib.Path(os.environ['JUPYTERLAB_DIR'])
# symlink all schemas in a folder, jupyter seems to assume that everything is installed
# in the same place.
schemas_dir = jupyterlab_dir / 'schemas'
if not schemas_dir.exists():
schemas_dir.mkdir()
for p in sys.path:
for schema in (pathlib.Path(p) / 'share' / 'jupyter' / 'lab' / 'schemas').glob('*/'):
dest = (schemas_dir / schema.name)
if dest.exists():
dest.unlink()
dest.symlink_to(schema)
c.LabServerApp.schemas_dir = str(schemas_dir)
# static really needs to be a sub-folder of $JUPYTERLAB_DIR
static = pathlib.Path(jupyterlab.__file__).parent.parent / 'share' / 'jupyter' / 'lab' / 'static'
static_dir = jupyterlab_dir / 'static'
if static_dir.exists():
static_dir.unlink()
static_dir.symlink_to(static)
c.LabServerApp.themes_dir = str(pathlib.Path(jupyterlab.__file__).parent / 'themes')
c.ServerApp.jpserver_extensions = {
'notebook': True,
'jupyter_lsp':True,
'jupyter_server_terminals': True,
'jupyterlab': True,
'notebook_shim': True,
}
else:
c.ServerApp.ssl_options = {
'ssl_version': ssl.PROTOCOL_TLSv1_2,
}
with open(knowledge_0, 'w') as file: with open(knowledge_0, 'w') as file:
parser.write(file) parser.write(file)
...@@ -3,7 +3,7 @@ extends = ...@@ -3,7 +3,7 @@ extends =
../numpy/openblas.cfg ../numpy/openblas.cfg
../matplotlib/buildout.cfg ../matplotlib/buildout.cfg
../ipython/buildout.cfg ../ipython/buildout.cfg
../python-cffi/buildout.cfg ../python-argon2-cffi/buildout.cfg
../python-pyzmq/buildout.cfg ../python-pyzmq/buildout.cfg
../scipy/buildout.cfg ../scipy/buildout.cfg
../scikit-learn/buildout.cfg ../scikit-learn/buildout.cfg
...@@ -15,10 +15,6 @@ parts = ...@@ -15,10 +15,6 @@ parts =
jupyter jupyter
jupyter-notebook-scripts jupyter-notebook-scripts
[argon2-cffi]
recipe = zc.recipe.egg:custom
egg = ${:_buildout_section_name_}
setup-eggs = ${python-cffi:egg}
[jupyter-env] [jupyter-env]
<= numpy-env <= numpy-env
...@@ -73,6 +69,7 @@ scripts = ...@@ -73,6 +69,7 @@ scripts =
jupyter-migrate jupyter-migrate
jupyter-troubleshoot jupyter-troubleshoot
jupyter-run jupyter-run
python-executable = ${buildout:bin-directory}/${:interpreter}
[jupyter-notebook-initialized-scripts] [jupyter-notebook-initialized-scripts]
recipe = zc.recipe.egg:scripts recipe = zc.recipe.egg:scripts
......
...@@ -40,6 +40,7 @@ need-matplotlibrc = ${matplotlibrc:location} ...@@ -40,6 +40,7 @@ need-matplotlibrc = ${matplotlibrc:location}
[versions] [versions]
matplotlib = 2.1.2 matplotlib = 2.1.2
cycler = 0.11.0 cycler = 0.11.0
matplotlib-inline = 0.1.6:whl
[versions:sys.version_info < (3,8)] [versions:sys.version_info < (3,8)]
cycler = 0.10.0 cycler = 0.10.0
...@@ -21,7 +21,7 @@ environment = numpy-env ...@@ -21,7 +21,7 @@ environment = numpy-env
eggs = ${cython:egg} eggs = ${cython:egg}
[versions] [versions]
numpy = 1.22.0 numpy = 1.24.4
[numpy:sys.version_info < (3,8)] [numpy:sys.version_info < (3,8)]
depends = depends =
......
...@@ -34,3 +34,8 @@ rpath = ...@@ -34,3 +34,8 @@ rpath =
${libjpeg:location}/lib ${libjpeg:location}/lib
${libtiff:location}/lib ${libtiff:location}/lib
${zlib:location}/lib ${zlib:location}/lib
Pillow-patches = ${:_profile_base_location_}/../../component/egg-patch/Pillow/0001-set-metadata-in-setup.py-for-compatibility-with-old-.patch#0a06cc5a94d3db24688938731e4b15e2
Pillow-patch-options = -p1
[pillow-python:python2]
Pillow-patches =
...@@ -8,10 +8,33 @@ extends = ...@@ -8,10 +8,33 @@ extends =
[astroid] [astroid]
recipe = zc.recipe.egg:custom recipe = zc.recipe.egg:custom
egg = astroid egg = astroid
setup-eggs = ${lazy-object-proxy:egg}
[astroid:python2]
patches = patches =
${:_profile_base_location_}/astroid-six_moves_import_error.patch#377beb0c50f52b9608bb6be7bf93096e ${:_profile_base_location_}/astroid-six_moves_import_error.patch#377beb0c50f52b9608bb6be7bf93096e
patch-options = -p1 patch-options = -p1
patch-binary = ${patch:location}/bin/patch patch-binary = ${patch:location}/bin/patch
setup-eggs =
[lazy-object-proxy]
recipe = zc.recipe.egg:custom
egg = lazy-object-proxy
setup-eggs =
${setuptools-scm:egg}
typing-extensions
tomli
[setuptools-scm]
recipe = zc.recipe.egg:custom
egg = setuptools-scm
setup-eggs = packaging
[mccabe]
recipe = zc.recipe.egg:custom
egg = mccabe
setup-eggs = pytest-runner
[pylint] [pylint]
recipe = zc.recipe.egg:custom recipe = zc.recipe.egg:custom
...@@ -21,3 +44,14 @@ patches = ...@@ -21,3 +44,14 @@ patches =
${:_profile_base_location_}/pylint-redefining-builtins-modules.patch#043defc6e9002ac48b40e078797d4d17 ${:_profile_base_location_}/pylint-redefining-builtins-modules.patch#043defc6e9002ac48b40e078797d4d17
patch-options = -p1 patch-options = -p1
patch-binary = ${patch:location}/bin/patch patch-binary = ${patch:location}/bin/patch
[pylint:python3]
recipe = zc.recipe.egg
patches =
[mccabe:python3]
recipe = zc.recipe.egg
setup-eggs =
[astroid:python3]
recipe = zc.recipe.egg
[buildout]
extends =
../python-cffi/buildout.cfg
parts = argon2-cffi
[argon2-cffi]
recipe = zc.recipe.egg:custom
egg = ${:_buildout_section_name_}
setup-eggs = ${python-cffi:egg}
[buildout] [buildout]
parts = parts =
python-ldap python-ldap-python
extends = extends =
../cyrus-sasl/buildout.cfg ../cyrus-sasl/buildout.cfg
../openldap/buildout.cfg ../openldap/buildout.cfg
...@@ -10,19 +10,25 @@ extends = ...@@ -10,19 +10,25 @@ extends =
[python-ldap-python] [python-ldap-python]
recipe = zc.recipe.egg:custom recipe = zc.recipe.egg:custom
egg = python-ldap egg = python-ldap
patches =
${:_profile_base_location_}/python-ldap-no_default_dirs.patch#959115f13f1de5c63654c69b8dfacd69
patch-options = -p1
patch-binary = ${patch:location}/bin/patch
rpath = rpath =
${openldap:location}/lib ${openldap:location}/lib
${cyrus-sasl:location}/lib ${cyrus-sasl:location}/lib
${openssl:location}/lib ${openssl:location}/lib
include-dirs = include-dirs =
${openldap:location}/include ${openldap:location}/include
${cyrus-sasl:location}/include/sasl ${cyrus-sasl:location}/include
${openssl:location}/include ${openssl:location}/include
library-dirs = library-dirs =
${openldap:location}/lib ${openldap:location}/lib
${cyrus-sasl:location}/lib ${cyrus-sasl:location}/lib
${openssl:location}/lib ${openssl:location}/lib
[python-ldap-python:python2]
patches =
${:_profile_base_location_}/python-ldap-no_default_dirs.patch#959115f13f1de5c63654c69b8dfacd69
patch-options = -p1
patch-binary = ${patch:location}/bin/patch
include-dirs =
${openldap:location}/include
${cyrus-sasl:location}/include/sasl
${openssl:location}/include
...@@ -16,3 +16,5 @@ egg = pyzmq ...@@ -16,3 +16,5 @@ egg = pyzmq
environment = python-pyzmq-env environment = python-pyzmq-env
rpath = rpath =
${libzmq:location}/lib ${libzmq:location}/lib
setup-eggs =
packaging
...@@ -21,9 +21,19 @@ setup-eggs = ...@@ -21,9 +21,19 @@ setup-eggs =
pkgconfig pkgconfig
pathlib2 pathlib2
setuptools-scm setuptools-scm
toml tomli
environment = python-xmlsec-env environment = python-xmlsec-env
[python-xmlsec:python2]
setup-eggs =
${lxml-python:egg}
pkgconfig
pathlib2
setuptools-scm
toml
[python-xmlsec-env] [python-xmlsec-env]
PKG_CONFIG=${pkgconfig:location}/bin/pkg-config PKG_CONFIG=${pkgconfig:location}/bin/pkg-config
PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${libxslt:location}/lib/pkgconfig:${xmlsec:location}/lib/pkgconfig PKG_CONFIG_PATH=${libxml2:location}/lib/pkgconfig:${libxslt:location}/lib/pkgconfig:${xmlsec:location}/lib/pkgconfig
......
...@@ -23,9 +23,21 @@ setup-eggs = ...@@ -23,9 +23,21 @@ setup-eggs =
${PyWavelets:egg} ${PyWavelets:egg}
${pillow-python:egg} ${pillow-python:egg}
networkx networkx
pythran
packaging
rpath = rpath =
${openblas:location}/lib ${openblas:location}/lib
[scikit-image:python2]
setup-eggs =
${numpy:egg}
${scipy:egg}
${cython:egg}
${PyWavelets:egg}
${pillow-python:egg}
networkx
[scikit-image-repository] [scikit-image-repository]
recipe = slapos.recipe.build:gitclone recipe = slapos.recipe.build:gitclone
git-executable = ${git:location}/bin/git git-executable = ${git:location}/bin/git
......
...@@ -15,6 +15,7 @@ recipe = zc.recipe.egg:custom ...@@ -15,6 +15,7 @@ recipe = zc.recipe.egg:custom
egg = scikit-learn egg = scikit-learn
environment = scikit-learn-env environment = scikit-learn-env
setup-eggs = setup-eggs =
${cython:egg}
${numpy:egg} ${numpy:egg}
${scipy:egg} ${scipy:egg}
rpath = rpath =
......
[buildout]
extends =
../../stack/erp5/buildout.cfg
[python]
part = python3
[erp5]
repository = https://lab.nexedi.com/nexedi/erp5.git
branch = zope4py3
develop = true
software.cfg.json
\ No newline at end of file
...@@ -46,10 +46,14 @@ from cryptography.x509.oid import NameOID ...@@ -46,10 +46,14 @@ from cryptography.x509.oid import NameOID
from slapos.testing.testcase import ManagedResource, makeModuleSetUpAndTestCaseClass from slapos.testing.testcase import ManagedResource, makeModuleSetUpAndTestCaseClass
from slapos.testing.utils import findFreeTCPPort from slapos.testing.utils import findFreeTCPPort
ERP5PY3 = os.environ['SLAPOS_SR_TEST_NAME'] == 'erp5-py3'
_setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass( _setUpModule, SlapOSInstanceTestCase = makeModuleSetUpAndTestCaseClass(
os.path.abspath( os.path.abspath(
os.path.join(os.path.dirname(__file__), '..', '..', 'software.cfg'))) os.path.join(os.path.dirname(__file__), '..', '..', 'software%s.cfg' % (
'-py3' if ERP5PY3 else ''))),
software_id=os.environ['SLAPOS_SR_TEST_NAME'],
)
setup_module_executed = False setup_module_executed = False
...@@ -191,7 +195,10 @@ def neo(instance_parameter_dict): ...@@ -191,7 +195,10 @@ def neo(instance_parameter_dict):
class ERP5InstanceTestCase(SlapOSInstanceTestCase, metaclass=ERP5InstanceTestMeta): class ERP5InstanceTestCase(SlapOSInstanceTestCase, metaclass=ERP5InstanceTestMeta):
"""ERP5 base test case """ERP5 base test case
""" """
__test_matrix__ = matrix((zeo, neo)) # switch between NEO and ZEO mode if ERP5PY3:
__test_matrix__ = matrix((zeo, )) # TODO: NEO is not yet enabled for py3
else:
__test_matrix__ = matrix((zeo, neo)) # switch between NEO and ZEO mode
@classmethod @classmethod
def isNEO(cls): def isNEO(cls):
......
...@@ -51,7 +51,7 @@ import urllib3 ...@@ -51,7 +51,7 @@ import urllib3
from slapos.testing.utils import CrontabMixin from slapos.testing.utils import CrontabMixin
import zc.buildout.configparser import zc.buildout.configparser
from . import CaucaseService, ERP5InstanceTestCase, default, matrix, neo, setUpModule from . import CaucaseService, ERP5InstanceTestCase, default, matrix, neo, setUpModule, ERP5PY3
setUpModule # pyflakes setUpModule # pyflakes
...@@ -1302,60 +1302,80 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase): ...@@ -1302,60 +1302,80 @@ class TestNEO(ZopeSkinsMixin, CrontabMixin, ERP5InstanceTestCase):
__partition_reference__ = 'n' __partition_reference__ = 'n'
__test_matrix__ = matrix((neo,)) __test_matrix__ = matrix((neo,))
def _getCrontabCommand(self, crontab_name: str) -> str: if ERP5PY3:
"""Read a crontab and return the command that is executed. # NEO is not ready for python3 at this time, this test is here to become
# an unexpected success once it starts working, so that we remember to
overloaded to use crontab from neo partition # remove this and enable neo in ERP5InstanceTestCase.__test_matrix__
""" setup_failed_exception = None
with open( @classmethod
os.path.join( def setUpClass(cls):
try:
super().setUpClass()
except BaseException as e:
cls.setup_failed_exception = e
cls.setUp = lambda self: None
cls.tearDownClass = classmethod(lambda cls: None)
@unittest.expectedFailure
def test_neo_py3(self):
self.assertIsNone(self.setup_failed_exception)
else:
def _getCrontabCommand(self, crontab_name: str) -> str:
"""Read a crontab and return the command that is executed.
overloaded to use crontab from neo partition
"""
with open(
os.path.join(
self.getComputerPartitionPath('neo-0'),
'etc',
'cron.d',
crontab_name,
)) as f:
crontab_spec, = f.readlines()
self.assertNotEqual(crontab_spec[0], '@', crontab_spec)
return crontab_spec.split(None, 5)[-1]
def test_log_rotation(self):
# first run to create state files
self._executeCrontabAtDate('logrotate', '2000-01-01')
def check_sqlite_log(path):
with self.subTest(path), contextlib.closing(sqlite3.connect(path)) as con:
con.execute('select * from log')
logfiles = ('neoadmin.log', 'neomaster.log', 'neostorage-0.log')
for f in logfiles:
check_sqlite_log(
os.path.join(
self.getComputerPartitionPath('neo-0'), self.getComputerPartitionPath('neo-0'),
'etc', 'var',
'cron.d', 'log',
crontab_name, f))
)) as f:
crontab_spec, = f.readlines()
self.assertNotEqual(crontab_spec[0], '@', crontab_spec)
return crontab_spec.split(None, 5)[-1]
def test_log_rotation(self):
# first run to create state files
self._executeCrontabAtDate('logrotate', '2000-01-01')
def check_sqlite_log(path):
with self.subTest(path), contextlib.closing(sqlite3.connect(path)) as con:
con.execute('select * from log')
logfiles = ('neoadmin.log', 'neomaster.log', 'neostorage-0.log') self._executeCrontabAtDate('logrotate', '2050-01-01')
for f in logfiles:
check_sqlite_log(
os.path.join(
self.getComputerPartitionPath('neo-0'),
'var',
'log',
f))
self._executeCrontabAtDate('logrotate', '2050-01-01') for f in logfiles:
check_sqlite_log(
os.path.join(
self.getComputerPartitionPath('neo-0'),
'srv',
'backup',
'logrotate',
f'{f}-20500101'))
for f in logfiles: self._executeCrontabAtDate('logrotate', '2050-01-02')
check_sqlite_log( requests.get(self._getAuthenticatedZopeUrl('/'), verify=False).raise_for_status()
os.path.join(
self.getComputerPartitionPath('neo-0'),
'srv',
'backup',
'logrotate',
f'{f}-20500101'))
self._executeCrontabAtDate('logrotate', '2050-01-02') for f in logfiles:
requests.get(self._getAuthenticatedZopeUrl('/'), verify=False).raise_for_status() check_sqlite_log(
os.path.join(
self.getComputerPartitionPath('neo-0'),
'var',
'log',
f))
for f in logfiles:
check_sqlite_log(
os.path.join(
self.getComputerPartitionPath('neo-0'),
'var',
'log',
f))
class TestPassword(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin): class TestPassword(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin):
__partition_reference__ = 'p' __partition_reference__ = 'p'
......
...@@ -23,7 +23,7 @@ import unittest ...@@ -23,7 +23,7 @@ import unittest
from slapos.grid.utils import md5digest from slapos.grid.utils import md5digest
from . import ERP5InstanceTestCase from . import ERP5InstanceTestCase
from . import setUpModule as _setUpModule from . import setUpModule as _setUpModule, ERP5PY3
from .test_erp5 import TestPublishedURLIsReachableMixin from .test_erp5 import TestPublishedURLIsReachableMixin
...@@ -38,6 +38,8 @@ def setUpModule(): ...@@ -38,6 +38,8 @@ def setUpModule():
md5digest(cls.getSoftwareURL()), md5digest(cls.getSoftwareURL()),
'bin', 'wcfs')): 'bin', 'wcfs')):
raise unittest.SkipTest("built with wendelin.core 1") raise unittest.SkipTest("built with wendelin.core 1")
if ERP5PY3:
raise unittest.SkipTest("wendelin.core does not support python3 yet")
class TestWCFS(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin): class TestWCFS(ERP5InstanceTestCase, TestPublishedURLIsReachableMixin):
......
...@@ -19,11 +19,11 @@ md5sum = 5f39952f94095b1f12f41db76867e71e ...@@ -19,11 +19,11 @@ md5sum = 5f39952f94095b1f12f41db76867e71e
[instance-jupyter] [instance-jupyter]
filename = instance-jupyter.cfg.in filename = instance-jupyter.cfg.in
md5sum = f9a0e5a134456d74ca8b4d87862f903d md5sum = 1812fa797b9eb687a634ebe96134b504
[jupyter-notebook-config] [jupyter-notebook-config]
filename = jupyter_notebook_config.py.jinja filename = jupyter_notebook_config.py.jinja
md5sum = 089e4c511a3c7b110471bf41ca2695a4 md5sum = 6c03113fb53d6ba98476f3353c083984
[erp5-kernel] [erp5-kernel]
filename = ERP5kernel.py filename = ERP5kernel.py
......
...@@ -73,7 +73,7 @@ key_file = ${directory:etc}/jupyter_cert.key ...@@ -73,7 +73,7 @@ key_file = ${directory:etc}/jupyter_cert.key
[instance] [instance]
recipe = slapos.cookbook:wrapper recipe = slapos.cookbook:wrapper
command-line = command-line =
{{ bin_directory }}/jupyter-lab {{ bin_directory }}/jupyter-notebook
--no-browser --no-browser
--ip=${instance-parameter:host} --ip=${instance-parameter:host}
--port=${instance-parameter:port} --port=${instance-parameter:port}
...@@ -87,16 +87,16 @@ environment = ...@@ -87,16 +87,16 @@ environment =
JUPYTER_PATH=${directory:jupyter_dir} JUPYTER_PATH=${directory:jupyter_dir}
JUPYTER_CONFIG_DIR=${directory:jupyter_config_dir} JUPYTER_CONFIG_DIR=${directory:jupyter_config_dir}
JUPYTER_RUNTIME_DIR=${directory:jupyter_runtime_dir} JUPYTER_RUNTIME_DIR=${directory:jupyter_runtime_dir}
JUPYTERLAB_DIR=${directory:jupyterlab-dir}
LANG=C.UTF-8 LANG=C.UTF-8
[jupyter-password] [jupyter-password]
recipe = slapos.cookbook:generate.password recipe = slapos.cookbook:generate.password
bytes = 10
[jupyter-notebook-config] [jupyter-notebook-config]
recipe = slapos.recipe.template:jinja2 recipe = slapos.recipe.template:jinja2
url = {{ jupyter_config_location }}/{{ jupyter_config_filename }} url = {{ jupyter_config_location }}/{{ jupyter_config_filename }}
output = ${directory:jupyter_config_dir}/jupyter_notebook_config.py output = ${directory:jupyter_config_dir}/jupyter_server_config.py
context = context =
key password jupyter-password:passwd key password jupyter-password:passwd
raw gcc_location {{ gcc_location }} raw gcc_location {{ gcc_location }}
...@@ -119,6 +119,7 @@ jupyter_runtime_dir = ${:jupyter_dir}/runtime ...@@ -119,6 +119,7 @@ jupyter_runtime_dir = ${:jupyter_dir}/runtime
jupyter_custom_dir = ${:jupyter_config_dir}/custom jupyter_custom_dir = ${:jupyter_config_dir}/custom
jupyter_nbextensions_dir = ${:jupyter_dir}/nbextensions jupyter_nbextensions_dir = ${:jupyter_dir}/nbextensions
erp5_kernel_dir = ${:jupyter_kernel_dir}/ERP5 erp5_kernel_dir = ${:jupyter_kernel_dir}/ERP5
jupyterlab-dir = ${:jupyter_dir}/lab
[request-slave-frontend-base] [request-slave-frontend-base]
recipe = slapos.cookbook:requestoptional recipe = slapos.cookbook:requestoptional
...@@ -194,7 +195,7 @@ output = ${directory:erp5_kernel_dir}/kernel.json ...@@ -194,7 +195,7 @@ output = ${directory:erp5_kernel_dir}/kernel.json
context = context =
raw python_executable {{ python_executable }} raw python_executable {{ python_executable }}
raw kernel_dir ${erp5-kernel:target-directory}/{{ erp5_kernel_filename }} raw kernel_dir ${erp5-kernel:target-directory}/{{ erp5_kernel_filename }}
key erp5_url slapconfiguration:configuration.erp5-url key erp5_url slapconfiguration:configuration.erp5-url
raw display_name ERP5 raw display_name ERP5
raw language_name python raw language_name python
......
...@@ -2,13 +2,47 @@ ...@@ -2,13 +2,47 @@
This script initializes Jupyter's configuration such as passwords and other This script initializes Jupyter's configuration such as passwords and other
things. It is run by IPython hence why it can use functions like get_config(). things. It is run by IPython hence why it can use functions like get_config().
''' '''
import configparser
from notebook.auth import passwd
import os import os
import pathlib
import sys
from jupyter_server.auth import passwd
import jupyterlab
c = get_config() c = get_config()
c.NotebookApp.password = passwd("{{ password }}") c.ServerApp.password = passwd("{{ password }}")
jupyterlab_dir = pathlib.Path(os.environ['JUPYTERLAB_DIR'])
# symlink all schemas in a folder, jupyter seems to assume that everything is installed
# in the same place.
schemas_dir = jupyterlab_dir / 'schemas'
if not schemas_dir.exists():
schemas_dir.mkdir()
for p in sys.path:
for schema in (pathlib.Path(p) / 'share' / 'jupyter' / 'lab' / 'schemas').glob('*/'):
dest = (schemas_dir / schema.name)
if dest.exists():
dest.unlink()
dest.symlink_to(schema)
c.LabServerApp.schemas_dir = str(schemas_dir)
# static really needs to be a sub-folder of $JUPYTERLAB_DIR
static = pathlib.Path(jupyterlab.__file__).parent.parent / 'share' / 'jupyter' / 'lab' / 'static'
static_dir = jupyterlab_dir / 'static'
if static_dir.exists():
static_dir.unlink()
static_dir.symlink_to(static)
c.LabServerApp.themes_dir = str(pathlib.Path(jupyterlab.__file__).parent / 'themes')
c.ServerApp.jpserver_extensions = {
'notebook': True,
'jupyter_lsp':True,
'jupyter_server_terminals': True,
'jupyterlab': True,
'notebook_shim': True,
}
try: try:
os.environ['PATH'] = "{{ gcc_location }}/bin" + os.pathsep + os.environ['PATH'] os.environ['PATH'] = "{{ gcc_location }}/bin" + os.pathsep + os.environ['PATH']
......
...@@ -47,7 +47,7 @@ class TestJupyter(InstanceTestCase): ...@@ -47,7 +47,7 @@ class TestJupyter(InstanceTestCase):
def test(self): def test(self):
connection_dict = self.computer_partition.getConnectionParameterDict() connection_dict = self.computer_partition.getConnectionParameterDict()
self.assertTrue('password' in connection_dict) self.assertIn('password', connection_dict)
password = connection_dict['password'] password = connection_dict['password']
self.assertEqual( self.assertEqual(
......
...@@ -22,7 +22,7 @@ md5sum = 102a7f1c1bc46a9b3fa5bd9b9a628e1d ...@@ -22,7 +22,7 @@ md5sum = 102a7f1c1bc46a9b3fa5bd9b9a628e1d
[instance-neo-admin] [instance-neo-admin]
filename = instance-neo-admin.cfg.in filename = instance-neo-admin.cfg.in
md5sum = b6e1ccb1d90160110202e5111eec2afa md5sum = a0ec1dce4c7a237fbeef3f8aee62e55a
[instance-neo-master] [instance-neo-master]
filename = instance-neo-master.cfg.in filename = instance-neo-master.cfg.in
...@@ -34,7 +34,7 @@ md5sum = 200ae55715cb735b0f97f8c835a3071f ...@@ -34,7 +34,7 @@ md5sum = 200ae55715cb735b0f97f8c835a3071f
[template-neo-my-cnf] [template-neo-my-cnf]
filename = my.cnf.in filename = my.cnf.in
md5sum = 56ea8f452d9e1526157ab9d03e631e1a md5sum = 3ae93702f3890a504cc8a93eb5ad52bc
[template-neo] [template-neo]
filename = instance.cfg.in filename = instance.cfg.in
......
...@@ -45,7 +45,7 @@ ssl = {{ dumps(bool(slapparameter_dict['ssl'])) }} ...@@ -45,7 +45,7 @@ ssl = {{ dumps(bool(slapparameter_dict['ssl'])) }}
cluster = {{ dumps(slapparameter_dict['cluster']) }} cluster = {{ dumps(slapparameter_dict['cluster']) }}
masters = {{ dumps(slapparameter_dict['masters']) }} masters = {{ dumps(slapparameter_dict['masters']) }}
extra-options = extra-options =
{%- for k, v in monitor_dict.iteritems() %} {%- for k, v in six.iteritems(monitor_dict) %}
{%- if k == 'backup' %} {%- if k == 'backup' %}
{%- set k = 'monitor-backup' %} {%- set k = 'monitor-backup' %}
{%- endif %} {%- endif %}
......
...@@ -45,7 +45,7 @@ innodb_locks_unsafe_for_binlog = 1 ...@@ -45,7 +45,7 @@ innodb_locks_unsafe_for_binlog = 1
{{x}}sync_frm = 0 {{x}}sync_frm = 0
# Extra parameters. # Extra parameters.
{%- for k, v in extra_dict.iteritems() %} {%- for k, v in six.iteritems(extra_dict) %}
{%- do assert('-' not in k) %} {%- do assert('-' not in k) %}
{{ k }} = {{ v }} {{ k }} = {{ v }}
{%- endfor %} {%- endfor %}
......
...@@ -134,10 +134,14 @@ inline = ...@@ -134,10 +134,14 @@ inline =
exec "$basedir/bin/mysqld" --defaults-file='{{defaults_file}}' "$@" exec "$basedir/bin/mysqld" --defaults-file='{{defaults_file}}' "$@"
[versions] [versions]
coverage = 5.5 coverage = 7.5.1
ecdsa = 0.13 ecdsa = 0.13
mysqlclient = 1.3.12 mysqlclient = 2.0.1
PyMySQL = 0.10.1 PyMySQL = 0.10.1
pycrypto = 2.6.1 pycrypto = 2.6.1
cython-zstd = 0.2 cython-zstd = 0.2
funcsigs = 1.0.2 funcsigs = 1.0.2
[versions:python2]
coverage = 5.5
mysqlclient = 1.3.12
...@@ -15,4 +15,4 @@ ...@@ -15,4 +15,4 @@
[template] [template]
filename = instance.cfg filename = instance.cfg
md5sum = f10fbca22d1d30dd7a4f36e1cd521b97 md5sum = 59a5b559b22ad0590e691226cea45055
...@@ -67,6 +67,7 @@ inline = ...@@ -67,6 +67,7 @@ inline =
command, command,
cwd={{ repr(folder) }}, cwd={{ repr(folder) }},
summaryf=UnitTest.summary, summaryf=UnitTest.summary,
envadj={ 'SLAPOS_SR_TEST_NAME': {{ repr(name) }} },
) )
{%- endif %} {%- endif %}
{%- endfor %} {%- endfor %}
......
...@@ -440,6 +440,7 @@ tests = ...@@ -440,6 +440,7 @@ tests =
dream ${slapos.test.dream-setup:setup} dream ${slapos.test.dream-setup:setup}
dufs ${slapos.test.dufs-setup:setup} dufs ${slapos.test.dufs-setup:setup}
erp5 ${slapos.test.erp5-setup:setup} erp5 ${slapos.test.erp5-setup:setup}
erp5-py3 ${slapos.test.erp5-setup:setup}
erp5testnode ${slapos.test.erp5testnode-setup:setup} erp5testnode ${slapos.test.erp5testnode-setup:setup}
fluentd ${slapos.test.fluentd-setup:setup} fluentd ${slapos.test.fluentd-setup:setup}
galene ${slapos.test.galene-setup:setup} galene ${slapos.test.galene-setup:setup}
...@@ -487,7 +488,7 @@ recurls = ...@@ -487,7 +488,7 @@ recurls =
slapos.core = slapos.core =
# Various needed versions # Various needed versions
Pillow = 9.2.0 Pillow = 10.2.0+SlapOSPatched001
forcediphttpsadapter = 1.0.1 forcediphttpsadapter = 1.0.1
image = 1.5.25 image = 1.5.25
plantuml = 0.3.0:whl plantuml = 0.3.0:whl
...@@ -495,7 +496,6 @@ pysftp = 0.2.9 ...@@ -495,7 +496,6 @@ pysftp = 0.2.9
requests-toolbelt = 0.8.0 requests-toolbelt = 0.8.0
testfixtures = 6.11.0 testfixtures = 6.11.0
mysqlclient = 2.1.1 mysqlclient = 2.1.1
pexpect = 4.8.0
ptyprocess = 0.6.0 ptyprocess = 0.6.0
paho-mqtt = 1.5.0 paho-mqtt = 1.5.0
pcpp = 1.30 pcpp = 1.30
......
This diff is collapsed.
...@@ -74,7 +74,7 @@ md5sum = ca0cb83950dd9079cc289891cce08e76 ...@@ -74,7 +74,7 @@ md5sum = ca0cb83950dd9079cc289891cce08e76
[template-erp5] [template-erp5]
filename = instance-erp5.cfg.in filename = instance-erp5.cfg.in
md5sum = edce1c63c13f0d8ec477711ea646444f md5sum = d6f7d2fa1bde019892897c767f93e089
[template-zeo] [template-zeo]
filename = instance-zeo.cfg.in filename = instance-zeo.cfg.in
...@@ -86,11 +86,11 @@ md5sum = 0ac4b74436f554cd677f19275d18d880 ...@@ -86,11 +86,11 @@ md5sum = 0ac4b74436f554cd677f19275d18d880
[template-zope] [template-zope]
filename = instance-zope.cfg.in filename = instance-zope.cfg.in
md5sum = e94599e02c987ed5dfc26263a54ccc15 md5sum = 9547bacad0635b0f64cac48f15c4e9ae
[template-balancer] [template-balancer]
filename = instance-balancer.cfg.in filename = instance-balancer.cfg.in
md5sum = 727c6f045da382fe50916e6ea5ae6405 md5sum = 48b8b8b4b87973beaa1fd6299244ebd6
[template-haproxy-cfg] [template-haproxy-cfg]
filename = haproxy.cfg.in filename = haproxy.cfg.in
......
...@@ -472,10 +472,9 @@ command = generate-apachedex-report ...@@ -472,10 +472,9 @@ command = generate-apachedex-report
recipe = slapos.recipe.template recipe = slapos.recipe.template
output = ${directory:etc}/${:_buildout_section_name_} output = ${directory:etc}/${:_buildout_section_name_}
inline = inline =
{% for line in slapparameter_dict['apachedex-configuration'] %} {% for line in slapparameter_dict['apachedex-configuration'] -%}
{# apachedex config files use shlex.split, so we need to quote the arguments. #} {# apachedex config files use shlex.split, so we need to quote the arguments. #}
{# BBB: in python 3 we can use shlex.quote instead. #} {{ six.moves.shlex_quote(line) }}
{{ repr(line.encode('utf-8')) }}
{% endfor %} {% endfor %}
[apachedex-parameters] [apachedex-parameters]
......
...@@ -200,7 +200,7 @@ config-zodb-dict = {{ dumps(zodb_dict) }} ...@@ -200,7 +200,7 @@ config-zodb-dict = {{ dumps(zodb_dict) }}
{% for server_type, server_dict in six.iteritems(storage_dict) -%} {% for server_type, server_dict in six.iteritems(storage_dict) -%}
{% if server_type == 'neo' -%} {% if server_type == 'neo' -%}
config-neo-cluster = ${publish-early:neo-cluster} config-neo-cluster = ${publish-early:neo-cluster}
config-neo-name = {{ server_dict.keys()[0] }} config-neo-name = {{ list(server_dict.keys())[0] }}
config-neo-masters = ${publish-early:neo-masters} config-neo-masters = ${publish-early:neo-masters}
{% else -%} {% else -%}
config-zodb-zeo = ${request-zodb:connection-storage-dict} config-zodb-zeo = ${request-zodb:connection-storage-dict}
......
...@@ -80,6 +80,8 @@ environment += ...@@ -80,6 +80,8 @@ environment +=
TZ={{ slapparameter_dict['timezone'] }} TZ={{ slapparameter_dict['timezone'] }}
MATPLOTLIBRC={{ parameter_dict['matplotlibrc'] }} MATPLOTLIBRC={{ parameter_dict['matplotlibrc'] }}
PYTHONUNBUFFERED=1 PYTHONUNBUFFERED=1
OFS_IMAGE_USE_DENYLIST=1
DISALLOWED_INLINE_MIMETYPES=
INSTANCE_HOME=${:instance-home} INSTANCE_HOME=${:instance-home}
FONTCONFIG_FILE=${fontconfig-conf:output} FONTCONFIG_FILE=${fontconfig-conf:output}
JUPYTER_PATH=${directory:jupyter-dir} JUPYTER_PATH=${directory:jupyter-dir}
...@@ -402,8 +404,8 @@ config-port = {{ '${' ~ zope_tunnel_section_name ~ ':ipv6-port}' }} ...@@ -402,8 +404,8 @@ config-port = {{ '${' ~ zope_tunnel_section_name ~ ':ipv6-port}' }}
promise = check_error_on_zope_longrequest_log promise = check_error_on_zope_longrequest_log
name = {{'check-' ~ name ~ '-longrequest-error-log.py'}} name = {{'check-' ~ name ~ '-longrequest-error-log.py'}}
config-log-file = {{ '${' ~ conf_parameter_name ~ ':longrequest-logger-file}' }} config-log-file = {{ '${' ~ conf_parameter_name ~ ':longrequest-logger-file}' }}
config-error-threshold = {{ slapparameter_dict["zope-longrequest-logger-error-threshold"] }} config-error-threshold = {{ dumps(slapparameter_dict["zope-longrequest-logger-error-threshold"]) }}
config-maximum-delay = {{ slapparameter_dict["zope-longrequest-logger-maximum-delay"] }} config-maximum-delay = {{ dumps(slapparameter_dict["zope-longrequest-logger-maximum-delay"]) }}
{% endif -%} {% endif -%}
[{{ section('logrotate-entry-' ~ name) }}] [{{ section('logrotate-entry-' ~ name) }}]
......
...@@ -2,150 +2,95 @@ ...@@ -2,150 +2,95 @@
# Version pins for required and commonly used dependencies. # Version pins for required and commonly used dependencies.
[versions] [versions]
Zope = 4.8.9 Zope = 5.9
Zope2 = 4.0 Zope2 = 4.0
# AccessControl 5+ no longer supports Zope 4. AccessControl = 6.3
AccessControl = 4.4 Acquisition = 5.1
Acquisition = 4.13 AuthEncoding = 5.0
AuthEncoding = 4.3 BTrees = 5.1
BTrees = 4.11.3 Chameleon = 4.2.0
Chameleon = 3.10.2 ; DateTime = 5.3
DateTime = 4.9 DateTime = 5.5
DocumentTemplate = 4.1 DocumentTemplate = 4.6
ExtensionClass = 4.9 ExtensionClass = 5.1
Missing = 4.2 MultiMapping = 5.0
MultiMapping = 4.1 Paste = 3.7.1
Paste = 3.5.2 PasteDeploy = 3.1.0
PasteDeploy = 3.0.1 Persistence = 4.1
Persistence = 3.6 RestrictedPython = 7.0
Products.BTreeFolder2 = 4.4 WebTest = 3.0.0
# ZCatalog 6+ no longer supports Zope 4.
Products.ZCatalog = 5.4
Record = 3.6
# RestrictedPython >= 6 no longer supports Zope 4
RestrictedPython = 5.4
WSGIProxy2 = 0.5.1 WSGIProxy2 = 0.5.1
WebOb = 1.8.7 WebOb = 1.8.7
WebTest = 3.0.0 ZConfig = 4.0
ZConfig = 3.6.1 ZODB = 5.8.1
ZEO = 5.3.0 beautifulsoup4 = 4.12.2
ZODB = 5.8.0 cffi = 1.16.0
five.globalrequest = 99.1
five.localsitemanager = 3.4
funcsigs = 1.0.2
future = 0.18.2
ipaddress = 1.0.23
mock = 4.0.3
multipart = 0.2.4 multipart = 0.2.4
pbr = 5.11.0 persistent = 5.1
persistent = 4.9.3 pycparser = 2.21
pytz = 2022.7 python-gettext = 5.0
roman = 3.3 pytz = 2023.3.post1
shutilwhich = 1.1.0
six = 1.16.0 six = 1.16.0
transaction = 3.0.1 roman = 4.1
soupsieve = 2.5
transaction = 4.0
waitress = 2.1.2 waitress = 2.1.2
z3c.pt = 3.3.1 z3c.pt = 4.0
zExceptions = 4.3 zExceptions = 5.0
zc.lockfile = 2.0 zc.lockfile = 3.0.post1
zdaemon = 4.4 zc.recipe.egg = 2.0.7
zodbpickle = 2.6 zodbpickle = 3.1
zope.annotation = 4.8 zope.annotation = 5.0
zope.browser = 2.4 zope.browser = 3.0
zope.browsermenu = 4.4 zope.browsermenu = 5.0
zope.browserpage = 4.4.0 zope.browserpage = 5.0
zope.browserresource = 4.4 zope.browserresource = 5.1
zope.cachedescriptors = 4.4 zope.cachedescriptors = 5.0
zope.component = 5.0.1 zope.component = 6.0
zope.componentvocabulary = 2.3.0 zope.configuration = 5.0
zope.configuration = 4.4.1 zope.container = 5.2
zope.container = 5.1 zope.contentprovider = 5.0
zope.contentprovider = 4.2.1 zope.contenttype = 5.1
zope.contenttype = 4.6 zope.datetime = 5.0.0
zope.datetime = 4.3.0 zope.deferredimport = 5.0
zope.deferredimport = 4.4 zope.deprecation = 5.0
zope.deprecation = 4.4.0 zope.dottedname = 6.0
zope.dottedname = 5.0 zope.event = 5.0
zope.event = 4.6 zope.exceptions = 5.0.1
zope.exceptions = 4.6 zope.filerepresentation = 6.0
zope.filerepresentation = 5.0.0 zope.globalrequest = 2.0
zope.formlib = 5.0.1 zope.hookable = 6.0
zope.globalrequest = 1.6 zope.i18n = 5.1
zope.hookable = 5.4 zope.i18nmessageid = 6.1.0
zope.i18n = 4.9.0 zope.interface = 6.1
zope.i18nmessageid = 5.1.1 zope.lifecycleevent = 5.0
zope.interface = 5.5.2 zope.location = 5.0
zope.lifecycleevent = 4.4 zope.pagetemplate = 5.0
zope.location = 4.3 zope.processlifetime = 3.0
zope.pagetemplate = 4.6.0 zope.proxy = 5.1
zope.processlifetime = 2.4 zope.ptresource = 5.0
zope.proxy = 4.6.1 zope.publisher = 7.0
zope.ptresource = 4.3.0 zope.schema = 7.0.1
zope.publisher = 6.1.0 zope.security = 6.2
zope.ramcache = 2.4 zope.sequencesort = 5.0
zope.schema = 6.2.1 zope.site = 5.0
zope.security = 5.8 zope.size = 5.0
zope.sendmail = 5.3 zope.structuredtext = 5.0
zope.sequencesort = 4.2 zope.tal = 5.0.1
zope.site = 4.6.1 zope.tales = 6.0
zope.size = 4.4 zope.testbrowser = 6.0
zope.structuredtext = 4.4 zope.testing = 5.0.1
zope.tal = 4.5 zope.traversing = 5.0
zope.tales = 5.2 zope.viewlet = 5.0
zope.testbrowser = 5.6.1
zope.testing = 4.10
zope.testrunner = 5.6
zope.traversing = 4.4.1
zope.viewlet = 4.3
[versions:python27]
# Chameleon 3.10 doesn't work on Python 2.7
Chameleon = 3.9.1
# DocumentTemplate 4+ requires Python 3.5 or higher
DocumentTemplate = 3.4
# PasteDeploy >3 requires Python 3.7
PasteDeploy = 2.1.1
# WSGIProxy 0.5 and up requires Python 3.7 and up
WSGIProxy2 = 0.4.6
# WebTest 3.0 and up requires Python 3.6 and up
WebTest = 2.0.35
# ZServer is only available for Python 2
ZServer = 4.0.2
# mock 4.0 and up requires Python 3.6 or higher
mock = 3.0.5
# multipart 0.2 and up requires Python 3
multipart = 0.1.1
# waitress 2 requires Python 3.6 or higher
waitress = 1.4.4
# zope.dottedname >= 5 requires Python 3.6 or higher
zope.dottedname = 4.3
# zope.container 5.x requires Python 3.7 or higher
zope.container = 4.10
[versions:python35]
# DocumentTemplate 4+ cannot be installed on Zope 4 for Python 3.5
DocumentTemplate = 3.4
# PasteDeploy >3 requires Python 3.7
PasteDeploy = 2.1.1
# WSGIProxy 0.5 and up requires Python 3.7 and up
WSGIProxy2 = 0.4.6
# WebTest 3.0 and up requires Python 3.6 and up
WebTest = 2.0.35
# mock 4.0 and up requires Python 3.6 or higher
mock = 3.0.5
# waitress 2 requires Python 3.6 or higher
waitress = 1.4.4
# zope.dottedname >= 5 requires Python 3.6 or higher
zope.dottedname = 4.3
# zope.container 5.x requires Python 3.7 or higher
zope.container = 4.10
[versions:python36] # XXX this is commented out because slapos.buildout is based on a too old buildout
# PasteDeploy >3 requires Python 3.7 # which does not understands :python37 yet. We target a more recent python version
PasteDeploy = 2.1.1 # so we don't use these versions.
# WSGIProxy 0.5 and up requires Python 3.7 and up # [versions:python37]
WSGIProxy2 = 0.4.6 # # PasteDeploy 3.x works on Python 3.7 but pulls tons of dependencies
# waitress 2.1 requires Python 3.7 or higher # PasteDeploy = 2.1.1
waitress = 2.0.0 # # SoupSieve 2.5 and up requires Python 3.8
# zope.container 5.x requires Python 3.7 or higher # soupsieve = 2.4.1
zope.container = 4.10 # # cffi 1.16.0 requires Python 3.8
# cffi = 1.15.1
...@@ -143,15 +143,20 @@ zc.recipe.egg = 2.0.3+slapos003 ...@@ -143,15 +143,20 @@ zc.recipe.egg = 2.0.3+slapos003
aiohttp = 3.8.5:whl aiohttp = 3.8.5:whl
aiosignal = 1.3.1:whl aiosignal = 1.3.1:whl
annotated-types = 0.6.0:whl
anyio = 4.3.0:whl
apache-libcloud = 2.4.0 apache-libcloud = 2.4.0
argon2-cffi = 20.1.0 argon2-cffi = 20.1.0
asn1crypto = 1.3.0 asn1crypto = 1.3.0
astor = 0.5 astor = 0.8.1
asttokens = 2.4.1:whl
async-generator = 1.10 async-generator = 1.10
async-lru = 2.0.4:whl
async-timeout = 4.0.3 async-timeout = 4.0.3
atomicwrites = 1.4.0 atomicwrites = 1.4.0
atomize = 0.2.0 atomize = 0.2.0
attrs = 23.1.0:whl attrs = 23.1.0:whl
Babel = 2.14.0
backcall = 0.2.0 backcall = 0.2.0
backports-abc = 0.5 backports-abc = 0.5
backports.functools-lru-cache = 1.6.1:whl backports.functools-lru-cache = 1.6.1:whl
...@@ -173,12 +178,14 @@ cliff = 2.8.3:whl ...@@ -173,12 +178,14 @@ cliff = 2.8.3:whl
cmd2 = 0.7.0 cmd2 = 0.7.0
collective.recipe.shelloutput = 0.1 collective.recipe.shelloutput = 0.1
collective.recipe.template = 2.0 collective.recipe.template = 2.0
comm = 0.2.1:whl
configparser = 4.0.2:whl configparser = 4.0.2:whl
contextlib2 = 0.6.0.post1 contextlib2 = 0.6.0.post1
croniter = 0.3.25 croniter = 0.3.25
cryptography = 3.3.2+SlapOSPatched001 cryptography = 3.3.2+SlapOSPatched001
dataclasses = 0.8 dataclasses = 0.8
dateparser = 0.7.6 dateparser = 0.7.6
debugpy = 1.8.1
decorator = 4.3.0 decorator = 4.3.0
defusedxml = 0.7.1 defusedxml = 0.7.1
distro = 1.7.0 distro = 1.7.0
...@@ -188,6 +195,7 @@ enum34 = 1.1.10 ...@@ -188,6 +195,7 @@ enum34 = 1.1.10
erp5.util = 0.4.75 erp5.util = 0.4.75
et-xmlfile = 1.0.1 et-xmlfile = 1.0.1
exceptiongroup = 1.1.3:whl exceptiongroup = 1.1.3:whl
executing = 2.0.1:whl
fastjsonschema = 2.18.1 fastjsonschema = 2.18.1
feedparser = 6.0.10 feedparser = 6.0.10
Flask = 3.0.0:whl Flask = 3.0.0:whl
...@@ -201,8 +209,10 @@ gitdb = 4.0.10 ...@@ -201,8 +209,10 @@ gitdb = 4.0.10
GitPython = 3.1.30 GitPython = 3.1.30
greenlet = 3.0.1 greenlet = 3.0.1
h11 = 0.14.0 h11 = 0.14.0
h5py = 2.7.1 h5py = 3.11.0
httpcore = 1.0.4:whl
httplib2 = 0.22.0 httplib2 = 0.22.0
httpx = 0.27.0:whl
idna = 3.4:whl idna = 3.4:whl
igmp = 1.0.4 igmp = 1.0.4
Importing = 1.10 Importing = 1.10
...@@ -210,22 +220,34 @@ importlib-metadata = 6.8.0:whl ...@@ -210,22 +220,34 @@ importlib-metadata = 6.8.0:whl
importlib-resources = 5.10.2:whl importlib-resources = 5.10.2:whl
inotify-simple = 1.1.1 inotify-simple = 1.1.1
ipaddress = 1.0.23 ipaddress = 1.0.23
ipykernel = 5.3.4:whl ipykernel = 6.29.3:whl
ipython = 7.16.3 ipython = 8.18.1:whl
ipython-genutils = 0.1.0 ipython-genutils = 0.2.0
ipywidgets = 6.0.0 ipywidgets = 8.1.2:whl
itsdangerous = 2.1.2 itsdangerous = 2.1.2
jdcal = 1.4 jdcal = 1.4
jedi = 0.17.2 jedi = 0.17.2
Jinja2 = 3.1.2:whl Jinja2 = 3.1.2:whl
joblib = 1.3.2:whl
json5 = 0.9.20:whl
jsonpointer = 2.2
jsonschema = 4.17.3:whl jsonschema = 4.17.3:whl
jupyter = 1.0.0 jupyter = 1.0.0
jupyter-client = 7.3.1 jupyter-client = 8.6.1:whl
jupyter-console = 6.4.4 jupyter-console = 6.6.3:whl
jupyter-core = 4.9.2 jupyter-core = 5.7.1:whl
jupyterlab = 0.26.3 jupyter-events = 0.6.3:whl
jupyterlab-launcher = 0.3.1 isoduration = 20.11.0
jupyterlab-pygments = 0.1.2 jupyter-lsp = 2.2.3:whl
jupyter-server = 2.10.0:whl
jupyter-server-terminals = 0.5.2:whl
jupyterlab = 4.1.3:whl
jupyterlab-launcher = 0.13.1
jupyterlab-pygments = 0.3.0:whl
jupyterlab-server = 2.24.0:whl
jupyterlab-widgets = 3.0.10:whl
arrow = 1.2.3
fqdn = 1.5.1
lock-file = 2.0 lock-file = 2.0
lockfile = 0.12.2:whl lockfile = 0.12.2:whl
lsprotocol = 2023.0.0b1:whl lsprotocol = 2023.0.0b1:whl
...@@ -237,18 +259,20 @@ meld3 = 1.0.2 ...@@ -237,18 +259,20 @@ meld3 = 1.0.2
mistune = 0.8.4 mistune = 0.8.4
mock = 3.0.5 mock = 3.0.5
more-itertools = 5.0.0 more-itertools = 5.0.0
mpmath = 1.0.0 mpmath = 1.3.0
msgpack = 1.0.5 msgpack = 1.0.5
multidict = 6.0.4 multidict = 6.0.4
nbclient = 0.5.1 nbclient = 0.10.0:whl
nbconvert = 6.5.4 nbconvert = 6.5.4
nbformat = 5.9.2:whl nbformat = 5.9.2:whl
nest-asyncio = 1.5.6 nest-asyncio = 1.5.6
netaddr = 0.7.19 netaddr = 0.7.19
netifaces = 0.10.7 netifaces = 0.10.7
notebook = 6.1.5 notebook = 7.1.2:whl
notebook-shim = 0.2.4:whl
openpyxl = 2.5.2 openpyxl = 2.5.2
outcome = 1.2.0 outcome = 1.2.0
overrides = 7.7.0
packaging = 23.2:whl packaging = 23.2:whl
pandocfilters = 1.4.3 pandocfilters = 1.4.3
paramiko = 2.11.0 paramiko = 2.11.0
...@@ -262,21 +286,24 @@ pickleshare = 0.7.4 ...@@ -262,21 +286,24 @@ pickleshare = 0.7.4
pim-dm = 1.4.0nxd002 pim-dm = 1.4.0nxd002
pkgconfig = 1.5.1 pkgconfig = 1.5.1
pkgutil-resolve-name = 1.3.10 pkgutil-resolve-name = 1.3.10
platformdirs = 4.2.0:whl
plone.recipe.command = 1.1 plone.recipe.command = 1.1
pluggy = 0.13.1:whl pluggy = 0.13.1:whl
ply = 3.11 ply = 3.11
prettytable = 0.7.2 prettytable = 0.7.2
prometheus-client = 0.9.0 prometheus-client = 0.9.0
prompt-toolkit = 3.0.19 prompt-toolkit = 3.0.43
psutil = 5.8.0 psutil = 5.8.0
psycopg2 = 2.9.9 psycopg2 = 2.9.9
ptyprocess = 0.5.1 ptyprocess = 0.5.1
pure-eval = 0.2.2:whl
py = 1.11.0:whl py = 1.11.0:whl
py-mld = 1.0.3 py-mld = 1.0.3
pyasn1 = 0.4.5 pyasn1 = 0.5.1
pycparser = 2.20 pycparser = 2.20
pycurl = 7.45.0 pycurl = 7.45.0
pydantic = 1.9.1 pydantic = 2.6.3:whl
pydantic-core = 2.16.3:whl
pygls = 1.1.0:whl pygls = 1.1.0:whl
Pygments = 2.9.0 Pygments = 2.9.0
PyNaCl = 1.3.0 PyNaCl = 1.3.0
...@@ -288,19 +315,23 @@ PyRSS2Gen = 1.1 ...@@ -288,19 +315,23 @@ PyRSS2Gen = 1.1
PySocks = 1.7.1 PySocks = 1.7.1
pytest-runner = 5.2:whl pytest-runner = 5.2:whl
python-dateutil = 2.8.2:whl python-dateutil = 2.8.2:whl
python-json-logger = 2.0.7
pytz = 2022.2.1 pytz = 2022.2.1
PyYAML = 5.4.1 PyYAML = 5.4.1
pyzmq = 22.3.0 pyzmq = 24.0.1
qtconsole = 4.3.0 qtconsole = 5.5.1
qtpy = 2.4.1:whl
random2 = 1.0.1 random2 = 1.0.1
regex = 2020.9.27 regex = 2020.9.27
requests = 2.31.0 requests = 2.31.0
rfc3339-validator = 0.1.4
rfc3986-validator = 0.1.1:whl
rpdb = 0.1.5 rpdb = 0.1.5
rubygemsrecipe = 0.4.4 rubygemsrecipe = 0.4.4
scandir = 1.10.0 scandir = 1.10.0
scikit-learn = 0.20.4 scikit-learn = 0.24.2
seaborn = 0.7.1 seaborn = 0.7.1
Send2Trash = 1.5.0 Send2Trash = 1.8.2:whl
setproctitle = 1.1.10 setproctitle = 1.1.10
setuptools-dso = 2.9 setuptools-dso = 2.9
sgmllib3k = 1.0.0 sgmllib3k = 1.0.0
...@@ -320,6 +351,7 @@ smmap = 5.0.0 ...@@ -320,6 +351,7 @@ smmap = 5.0.0
sniffio = 1.3.0 sniffio = 1.3.0
sortedcontainers = 2.4.0 sortedcontainers = 2.4.0
soupsieve = 1.9.5 soupsieve = 1.9.5
stack-data = 0.6.3:whl
statsmodels = 0.13.5+SlapOSPatched001 statsmodels = 0.13.5+SlapOSPatched001
stevedore = 1.21.0:whl stevedore = 1.21.0:whl
subprocess32 = 3.5.4 subprocess32 = 3.5.4
...@@ -327,24 +359,28 @@ supervisor = 4.1.0 ...@@ -327,24 +359,28 @@ supervisor = 4.1.0
sympy = 1.1.1 sympy = 1.1.1
terminado = 0.9.1 terminado = 0.9.1
testpath = 0.4.4 testpath = 0.4.4
threadpoolctl = 3.3.0:whl
tinycss2 = 1.2.1:whl tinycss2 = 1.2.1:whl
tornado = 6.1 tomli = 2.0.1:whl
traitlets = 5.11.2:whl tornado = 6.4
traitlets = 5.14.1:whl
trio = 0.22.0 trio = 0.22.0
trio-websocket = 0.9.2 trio-websocket = 0.9.2
typeguard = 3.0.2:whl typeguard = 3.0.2:whl
typing-extensions = 4.8.0:whl typing-extensions = 4.8.0:whl
tzlocal = 1.5.1 tzlocal = 1.5.1
unicodecsv = 0.14.1 unicodecsv = 0.14.1
uri-template = 1.2.0
uritemplate = 4.1.1 uritemplate = 4.1.1
urllib3 = 1.26.12 urllib3 = 1.26.12
wcwidth = 0.2.5 wcwidth = 0.2.5
webcolors = 1.12
webencodings = 0.5.1 webencodings = 0.5.1
websocket-client = 1.5.1 websocket-client = 1.5.1
websockets = 10.4 websockets = 10.4
Werkzeug = 3.0.0:whl Werkzeug = 3.0.0:whl
wheel = 0.41.2:whl wheel = 0.41.2:whl
widgetsnbextension = 2.0.0 widgetsnbextension = 4.0.10:whl
wsproto = 1.2.0 wsproto = 1.2.0
xlrd = 1.1.0 xlrd = 1.1.0
xml-marshaller = 1.0.2 xml-marshaller = 1.0.2
...@@ -357,9 +393,9 @@ zipp = 3.12.0:whl ...@@ -357,9 +393,9 @@ zipp = 3.12.0:whl
zodburi = 2.5.0 zodburi = 2.5.0
zope.event = 4.6.0 zope.event = 4.6.0
zope.exceptions = 4.6 zope.exceptions = 4.6
zope.interface = 5.4.0 zope.interface = 5.5.2
zope.testing = 4.7 zope.testing = 4.10
zope.testrunner = 5.2 zope.testrunner = 5.6
[versions:sys.version_info < (3,10)] [versions:sys.version_info < (3,10)]
# keep old statsmodels by default until slapos.toolbox is updated # keep old statsmodels by default until slapos.toolbox is updated
...@@ -387,28 +423,46 @@ gevent = 20.9.0 ...@@ -387,28 +423,46 @@ gevent = 20.9.0
gitdb2 = 2.0.5 gitdb2 = 2.0.5
GitPython = 2.1.11 GitPython = 2.1.11
greenlet = 0.4.17 greenlet = 0.4.17
h5py = 2.7.1
idna = 2.9 idna = 2.9
importlib-metadata = 1.7.0:whl importlib-metadata = 1.7.0:whl
itsdangerous = 0.24 ipykernel = 5.3.4:whl
Jinja2 = 2.11.3 ipython = 7.16.3
ipython-genutils = 0.1.0
ipywidgets = 6.0.0
jsonschema = 3.0.2:whl jsonschema = 3.0.2:whl
jupyter-client = 7.3.1
jupyter-console = 6.4.4
jupyter-core = 4.9.2
jupyterlab = 0.26.3
jupyterlab-launcher = 0.3.1
jupyterlab-pygments = 0.1.2
MarkupSafe = 1.0 MarkupSafe = 1.0
mpmath = 1.0.0
msgpack = 0.6.2 msgpack = 0.6.2
nbclient = 0.5.1
notebook = 6.1.5
packaging = 16.8 packaging = 16.8
prompt-toolkit = 3.0.19
psycopg2 = 2.8.6 psycopg2 = 2.8.6
pycurl = 7.43.0 pycurl = 7.43.0
pyparsing = 2.4.7 pyparsing = 2.4.7
pyrsistent = 0.16.1 pyrsistent = 0.16.1
pyzmq = 22.3.0
qtconsole = 4.3.0
requests = 2.27.1 requests = 2.27.1
scikit-learn = 0.20.4
selectors34 = 1.2 selectors34 = 1.2
Send2Trash = 1.5.0
slapos.toolbox = 0.128.1 slapos.toolbox = 0.128.1
smmap = 0.9.0 smmap = 0.9.0
smmap2 = 2.0.5 smmap2 = 2.0.5
statsmodels = 0.11.1 tornado = 6.1
traitlets = 4.3.3 traitlets = 4.3.3
uritemplate = 3.0.0 uritemplate = 3.0.0
Werkzeug = 1.0.1 Werkzeug = 1.0.1
wheel = 0.35.1:whl wheel = 0.35.1:whl
widgetsnbextension = 2.0.0
zipp = 1.2.0:whl zipp = 1.2.0:whl
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment