Skip to content

Commit bf3b69c

Browse files
committed
Make pth-generation PEP517-compatible.
1 parent 1f9bd36 commit bf3b69c

File tree

3 files changed

+70
-76
lines changed

3 files changed

+70
-76
lines changed

setup.py

+1-24
Original file line numberDiff line numberDiff line change
@@ -147,30 +147,7 @@ def copy_extensions_to_source(self):
147147
self.get_finalized_command("build_py").get_package_dir("mplcairo"))
148148

149149

150-
@setup.register_pth_hook("mplcairo.pth")
151-
def _pth_hook():
152-
if os.environ.get("MPLCAIRO_PATCH_AGG"):
153-
from importlib.machinery import PathFinder
154-
class MplCairoMetaPathFinder(PathFinder):
155-
def find_spec(self, fullname, path=None, target=None):
156-
spec = super().find_spec(fullname, path, target)
157-
if fullname == "matplotlib.backends.backend_agg":
158-
def exec_module(module):
159-
type(spec.loader).exec_module(spec.loader, module)
160-
# The pth file does not get properly uninstalled from
161-
# a develop install. See pypa/pip#4176.
162-
try:
163-
import mplcairo.base
164-
except ImportError:
165-
return
166-
module.FigureCanvasAgg = \
167-
mplcairo.base.FigureCanvasCairo
168-
module.RendererAgg = \
169-
mplcairo.base.GraphicsContextRendererCairo
170-
spec.loader.exec_module = exec_module
171-
sys.meta_path.remove(self)
172-
return spec
173-
sys.meta_path.insert(0, MplCairoMetaPathFinder())
150+
setup.register_pth_hook("setup_mplcairo_pth.py", "mplcairo.pth")
174151

175152

176153
setup(

setup_mplcairo_pth.py

+27
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,27 @@
1+
import os
2+
import sys
3+
4+
5+
if os.environ.get("MPLCAIRO_PATCH_AGG"):
6+
from importlib.machinery import PathFinder
7+
8+
class MplCairoMetaPathFinder(PathFinder):
9+
def find_spec(self, fullname, path=None, target=None):
10+
spec = super().find_spec(fullname, path, target)
11+
if fullname == "matplotlib.backends.backend_agg":
12+
def exec_module(module):
13+
type(spec.loader).exec_module(spec.loader, module)
14+
# The pth file does not get properly uninstalled from
15+
# a develop install. See pypa/pip#4176.
16+
try:
17+
import mplcairo.base
18+
except ImportError:
19+
return
20+
module.FigureCanvasAgg = \
21+
mplcairo.base.FigureCanvasCairo
22+
module.RendererAgg = \
23+
mplcairo.base.GraphicsContextRendererCairo
24+
spec.loader.exec_module = exec_module
25+
sys.meta_path.remove(self)
26+
return spec
27+
sys.meta_path.insert(0, MplCairoMetaPathFinder())

setupext.py

+42-52
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,9 @@
1-
from functools import partial
2-
import inspect
3-
import re
41
from pathlib import Path
2+
import tokenize
53

64
import setuptools
7-
from setuptools import Extension, find_packages
5+
from setuptools import Distribution, Extension, find_packages
86
from setuptools.command.build_ext import build_ext
9-
from setuptools.command.develop import develop
10-
from setuptools.command.install_lib import install_lib
117

128

139
__all__ = ["Extension", "build_ext", "find_packages", "setup"]
@@ -22,49 +18,43 @@ def build_extensions(self):
2218
super().build_extensions()
2319

2420

25-
class setup:
26-
_pth_hooks = {}
27-
28-
def __new__(cls, **kwargs):
29-
30-
cmdclass = kwargs.setdefault("cmdclass", {})
31-
32-
class pth_hook_mixin:
33-
34-
def run(self):
35-
super().run()
36-
for fname, (name, source) in cls._pth_hooks.items():
37-
with Path(self.install_dir, fname).open("w") as file:
38-
file.write(f"import os; exec({source!r}); {name}()")
39-
40-
def get_outputs(self):
41-
return (super().get_outputs()
42-
+ [str(Path(self.install_dir, fname))
43-
for fname in cls._pth_hooks])
44-
45-
cmdclass["develop"] = type(
46-
"develop_with_pth_hook",
47-
(pth_hook_mixin, cmdclass.get("develop", develop)),
48-
{})
49-
cmdclass["install_lib"] = type(
50-
"install_lib_with_pth_hook",
51-
(pth_hook_mixin, cmdclass.get("install_lib", install_lib)),
52-
{})
53-
54-
setuptools.setup(**kwargs)
55-
56-
@classmethod
57-
def register_pth_hook(cls, fname, func=None):
58-
if func is None:
59-
return partial(cls.register_pth_hook, fname)
60-
source = inspect.getsource(func)
61-
if not re.match(r"\A@setup\.register_pth_hook.*\ndef ", source):
62-
raise SyntaxError("register_pth_hook must be used as a toplevel "
63-
"decorator to a function")
64-
_, source = source.split("\n", 1)
65-
d = {}
66-
exec(source, {}, d)
67-
if set(d) != {func.__name__}:
68-
raise SyntaxError(
69-
"register_pth_hook should define a single function")
70-
cls._pth_hooks[fname] = func.__name__, source
21+
def register_pth_hook(source_path, pth_name):
22+
"""
23+
::
24+
setup.register_pth_hook("hook_source.py", "hook_name.pth") # Add hook.
25+
"""
26+
with tokenize.open(source_path) as file:
27+
source = file.read()
28+
_pth_hook_mixin._pth_hooks.append((pth_name, source))
29+
30+
31+
class _pth_hook_mixin:
32+
_pth_hooks = []
33+
34+
def run(self):
35+
super().run()
36+
for pth_name, source in self._pth_hooks:
37+
with Path(self.install_dir, pth_name).open("w") as file:
38+
file.write(f"import os; exec({source!r})")
39+
40+
def get_outputs(self):
41+
return (super().get_outputs()
42+
+ [str(Path(self.install_dir, pth_name))
43+
for pth_name, _ in self._pth_hooks])
44+
45+
46+
def _prepare_pth_hook(kwargs):
47+
cmdclass = kwargs.setdefault("cmdclass", {})
48+
get = Distribution({"cmdclass": cmdclass}).get_command_class
49+
cmdclass["develop"] = type(
50+
"develop_with_pth_hook", (_pth_hook_mixin, get("develop")), {})
51+
cmdclass["install_lib"] = type(
52+
"install_lib_with_pth_hook", (_pth_hook_mixin, get("install_lib")), {})
53+
54+
55+
def setup(**kwargs):
56+
_prepare_pth_hook(kwargs)
57+
setuptools.setup(**kwargs)
58+
59+
60+
setup.register_pth_hook = register_pth_hook

0 commit comments

Comments
 (0)