-
Notifications
You must be signed in to change notification settings - Fork 0
/
backend.py
179 lines (143 loc) · 5.11 KB
/
backend.py
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
from __future__ import annotations
import io
import os
import pathlib
import posixpath
import re
import tarfile
import textwrap
import time
import types
from typing import (
Iterator,
)
from jaraco.compat.py38 import r_fix
from jaraco.functools import pass_none
from wheel.wheelfile import WheelFile
from .metadata import Message
class Filter:
"""
Filters tar- and zip-info objects for inclusion in different distributions.
"""
def __init__(self, name: str):
"""
Initialize the filter with the root of the package ("coherent/build").
"""
self.name = name
def __call__(self, info):
"""
Determine disposition for the info object.
Given an object like a tarfile.TarInfo object, determine if it
should be included or filtered. Return None if the object should
be omitted. Otherwise, mutate the object to include self.name
as a prefix.
"""
if info.name == '.':
info.name = self.name
return info
ignore_pattern = '|'.join(self.ignored)
if re.match(ignore_pattern, r_fix(info.name).removeprefix('./')):
return
info.name = self.name + '/' + r_fix(info.name).removeprefix('./')
return info
class SDist(Filter):
"""
>>> sf = SDist(name="foo")
Ignores the .git directory
>>> sf(types.SimpleNamespace(name='./.git'))
Ignores __pycache__ directories
>>> sf(types.SimpleNamespace(name='./bar/__pycache__'))
Ignore paths starting with a dot
>>> sf(types.SimpleNamespace(name='./bar/.DS_Store'))
Should not ignore nested dist dirs
>>> sf(types.SimpleNamespace(name='./bar/dist'))
namespace(name='foo/bar/dist')
"""
ignored = ['dist', r'(.*[/])?__pycache__$', r'(.*[/])?[.]']
class Wheel(Filter):
ignored = [
'docs',
'tests',
r'README.*',
'PKG-INFO',
re.escape('(meta)'),
re.escape('pyproject.toml'),
]
class ZipInfo(types.SimpleNamespace):
"""
Simulate a compatible interface as a tarfile.TarInfo object.
"""
def __init__(self, path):
zip_name = path.replace(os.pathsep, posixpath.sep)
super().__init__(path=path, name=zip_name)
def wheel_walk(filter_: Wheel) -> Iterator[ZipInfo]:
"""
Walk the current directory, applying and honoring the filter for traversal.
"""
for root, dirs, files in os.walk('.'):
zi = ZipInfo(path=root)
if not filter_(zi):
dirs[:] = []
continue
children = (ZipInfo(path=os.path.join(root, file)) for file in files)
yield from filter(bool, map(filter_, children))
def make_sdist_metadata(metadata: Message) -> tarfile.TarInfo:
info = tarfile.TarInfo(f'{metadata.id}/PKG-INFO')
file = io.BytesIO(metadata.render().encode('utf-8'))
info.size = len(file.getbuffer())
info.mtime = time.time()
return info, file
def prepare_metadata(metadata_directory, config_settings=None):
metadata = Message.load() or Message.discover()
md_root = pathlib.Path(metadata_directory, f'{metadata.id}.dist-info')
md_root.mkdir()
for name, contents in metadata.render_wheel():
md_root.joinpath(name).write_text(contents)
return md_root.name
def build_wheel(wheel_directory, config_settings=None, metadata_directory=None):
metadata = (
pass_none(Message.load)(metadata_directory)
or Message.load()
or Message.discover()
)
root = metadata['Name'].replace('.', '/')
filename = pathlib.Path(wheel_directory) / f'{metadata.id}-py3-none-any.whl'
with WheelFile(filename, 'w') as zf:
for info in wheel_walk(Wheel(root)):
zf.write(info.path, arcname=info.name)
for name, contents in metadata.render_wheel():
zf.writestr(f'{metadata.id}.dist-info/{name}', contents)
return str(filename)
def build_sdist(sdist_directory, config_settings=None):
metadata = Message.discover()
filename = pathlib.Path(sdist_directory) / f'{metadata.id}.tar.gz'
with tarfile.open(filename, 'w:gz') as tf:
tf.add(pathlib.Path(), filter=SDist(metadata.id))
tf.addfile(*make_sdist_metadata(metadata))
return str(filename)
def build_editable(wheel_directory, config_settings=None, metadata_directory=None):
metadata = (
pass_none(Message.load)(metadata_directory)
or Message.load()
or Message.discover()
)
root = metadata['Name'].replace('.', '/')
filename = pathlib.Path(wheel_directory) / f'{metadata.id}-py3-none-any.whl'
with WheelFile(filename, 'w') as zf:
zf.writestr(f'{root}/__init__.py', proxy())
for name, contents in metadata.render_wheel():
zf.writestr(f'{metadata.id}.dist-info/{name}', contents)
return str(filename)
def proxy():
return textwrap.dedent(f"""
import io
__path__ = [{os.getcwd()!r}]
__file__ = __path__[0] + '/__init__.py'
try:
strm = io.open_code(__file__)
except FileNotFoundError:
pass
else:
with strm:
exec(compile(strm.read(), __file__, 'exec'))
""").lstrip()