Skip to content

Commit

Permalink
compat: Python 3.8 no longer tolerates embezzling __classcell__ attrib
Browse files Browse the repository at this point in the history
While this is highly internal thing, it seems that whenever Python's
semantic analyzer hits __class__ or super(), it will arrange for
injecting said new (rather internal) attribute towards __new__()
material instantiation of the the class as defined within respective
metaclass.  Then, when either of these will get effectively called,
presence of said attribute is checked.  Until 3.8, this was just
warning-worth, but with 3.8, it becomes a hard error.

In our case, (least resistance) solution is to simply refrain from
using either of those built-in constructs -- in fact, we don't really
need them(!).  Also the all-time explanatory guard is added for a good
measure.

Note that this may not be hit in casual run of clufter, but it certainly
was the case in the test suite, where the already prepared objects are
asked once again and hence our harness intercepting such repeated
requests and avoiding thus unnecessary computations kicks in.

Error sample (./run-tests command_manager filter_manager):
ERROR:clufter.plugin_registry:Module load error:
  <_io.TextIOWrapper name='/clufter/formats/simpleconfig.py' mode='r' encoding='utf-8'>:
  __class__ not set defining 'simpleconfig' as <class 'clufter.formats.simpleconfig.simpleconfig'>.
  Was __classcell__ propagated to type.__new__?

References:
https://docs.python.org/3/whatsnew/3.6.html#pep-487-simpler-customization-of-class-creation
https://docs.python.org/3/whatsnew/3.8.html#changes-in-the-python-api
python/cpython@f5e7b19

Signed-off-by: Jan Pokorný <[email protected]>
  • Loading branch information
jnpkrn committed Aug 14, 2019
1 parent 6c544cd commit b1512e4
Show file tree
Hide file tree
Showing 3 changed files with 8 additions and 4 deletions.
2 changes: 1 addition & 1 deletion format.py
Original file line number Diff line number Diff line change
Expand Up @@ -505,7 +505,7 @@ def _construct(self, protocol, *args, **kwargs):
assert isinstance(protocol, basestring), \
"protocol has to be string for `{0}', not `{1}'" \
.format(self.__class__.name, protocol)
super(SimpleFormat, self)._construct(protocol, *args, **kwargs)
Format._construct(self, protocol, *args, **kwargs)

@Format.producing(BYTESTRING)
def get_bytestring(self, *iodecl):
Expand Down
4 changes: 2 additions & 2 deletions formats/simpleconfig.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: UTF-8 -*-
# Copyright 2017 Red Hat, Inc.
# Copyright 2019 Red Hat, Inc.
# Part of clufter project
# Licensed under GPLv2+ (a copy included | http://gnu.org/licenses/gpl-2.0.txt)
"""Structured configuration formats such as corosync.conf"""
Expand Down Expand Up @@ -81,7 +81,7 @@ def get_bytestring(self, *iodecl):
}
"""
# try to look (indirectly) if we have a file at hand first
ret = super(simpleconfig, self).get_bytestring(self.BYTESTRING)
ret = SimpleFormat.get_bytestring(self, self.BYTESTRING)
if ret is not None:
return ret

Expand Down
6 changes: 5 additions & 1 deletion plugin_registry.py
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
# -*- coding: UTF-8 -*-
# Copyright 2017 Red Hat, Inc.
# Copyright 2019 Red Hat, Inc.
# Part of clufter project
# Licensed under GPLv2+ (a copy included | http://gnu.org/licenses/gpl-2.0.txt)
"""Easy (at least for usage) plugin framework"""
Expand Down Expand Up @@ -104,6 +104,10 @@ def probe(registry, name, bases, attrs=None):
assert '-' not in name, "name cannot contain a dash"
dname = cli_decor(name)
attrs = attrs or {}
# seems more reasonable than plying even higher meta magic game
assert not(attrs) or '__classcell__' not in attrs, \
("{0} plugin: refrain from '__class__'/'super' (Py3.8+ limit)"
).format(name)
try:
ret = registry._plugins[dname]
log.info("Probe `{0}' plugin under `{1}' registry: already tracked"
Expand Down

0 comments on commit b1512e4

Please sign in to comment.